/*
 * Ingenic libsysutils Audio implement.
 *
 * Copyright (C) 2014 Ingenic Semiconductor Co.,Ltd
 * Author: Aeolus <weiqing.yan@ingenic.com>
 */

#define _GNU_SOURCE

#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/time.h>
#include <linux/soundcard.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <imp/imp_audio.h>
#include <imp/imp_log.h>
#include <imp/imp_system.h>
#include <system/system.h>

#include <webrtc_aec.h>
#include <webrtc/modules/audio_processing/ns/include/noise_suppression.h>
#include <webrtc/modules/audio_processing/agc/legacy/gain_control.h>
#include <webrtc_audio_processing/webrtc/modules/audio_processing/ns/ns_core.h>
#include <webrtc_audio_processing/webrtc/modules/audio_processing/agc/legacy/analog_agc.h>
#include <webrtc_audio_processing/webrtc/common_audio/signal_processing/include/signal_processing_library.h>

#include "audio_common.h"

#include "audioclient.h"
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
int audio_client = 0;

/* For Debug */
//#define AUDIO_DEBUG
//#define AUDIO_OUT_DEBUG

static int SAMPLE_RATE = 8000;
static int SAMPLE_TIME = 10;
static int AUDIO_ECHO_SUPPRESSOR_SIZE = (8000 * sizeof(short) * 10 / 1000);
static int MAX_AUDIO_BUF_SIZE = (96000 * sizeof(short) * 10 / 1000) * 5;
static int MAX_AUDIO_BUF_NUM = 40;

static AudioDev audioDev[MAX_AUDIO_DEV];

/* Record And Play Sample rate */
static int record_sample_rate = 0;
static int play_sample_rate = 0;

/* Audio Record */
static pthread_t audio_record_thread_t;
static int audio_record_thread_run = 0;

/* Audio Echo Suppressor */
/*static pthread_t echo_suppressor_thread_t;*/
static int enable_echo_suppressor = 0;
static int echo_suppressor_start = 0;
static int echo_suppressor_clear = 1; /* Clear the first record data */
static int echo_time_sync;


/* Lock and Semaphore */
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t play_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t play_cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t record_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t record_cond = PTHREAD_COND_INITIALIZER;
/* protect apm destroy */
static pthread_mutex_t aec_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t ns_mutex = PTHREAD_MUTEX_INITIALIZER;

static int audio_play_is_ready = 0;
static int audio_record_is_ready = 0;

/* Buf List Head */
static AudioList *g_record_head_in = NULL;
static AudioList *g_record_head_out = NULL;

static AudioList *g_play_head_in = NULL;
static AudioList *g_play_head_out = NULL;

static AudioList *g_filter_head_in = NULL;
static AudioList *g_filter_head_out = NULL;

static int audio_list_init_ref_num = 0;

static int enable_noise_suppression = 0;

typedef struct NsHandle_t {
	NsHandle* handle;
	int cell_num;
	float *sp,*out;
}NsHandle_t;

NsHandle_t handle_ns = {NULL, 0};

typedef struct {
  int16_t y[4];
  int16_t x[2];
  const int16_t* ba;
} FilterState;

#ifdef AUDIO_OUT_DEBUG
FILE * file_play_1 = NULL;
FILE * file_play_2 = NULL;
#endif

int Hpf_Version(char *versionStr, char length)
{
    char version[] = "Ingenic High Pass Filter 1.1.0";
    char versionLen = (char)strlen(version) + 1;

    if (versionStr == NULL)
    {
        return -1;
    }

    if (versionLen > length)
    {
        return -1;
    }

    strncpy(versionStr, version, versionLen);
    return 0;
}

int IngenicNs_Process(NsHandle_t* h, short* spframe, short* outframe, int size)
{
	int i,j,k;
	float *sp = h->sp;
	float *out = h->out;
	const float * const *p = (const float*const*)&sp;
	float * const *q = &out;
	k = size / (h->cell_num*2);
	if (k == 0)
		return -1;
	for (i = 0; i < k; i++) {
		for (j = 0; j < h->cell_num; j++) {
			sp[j] = spframe[j+(i*h->cell_num)];
		}
		memset(out, 0, h->cell_num*sizeof(float));
		WebRtcNs_Analyze(h->handle,*p);
		WebRtcNs_Process(h->handle, p, 1, q);
		for (j = 0; j < h->cell_num; j++) {
			outframe[j+(i*h->cell_num)] = out[j];
		}
	}
	return 0;
}

int IngenicAgc_Process(void *agcInst, const int16_t *in_near, int16_t samples,
                      int16_t *out, int32_t inMicLevel,
                      int32_t *outMicLevel, int16_t echo,
                      uint8_t *saturationWarning, int size)
{
	int ret = -1;
	int k, i = size / (samples*2);
	/* printf("size = %d, samples = %d, i = %d\n", size, samples, i); */
	const int16_t *p = in_near;
	int16_t *q = out;

	for(k = 0; k < i; k++) {
		const int16_t *ptmp = p + samples*k;
		int16_t *qtmp = q + samples*k;
		const int16_t *const*pp = &ptmp;
		int16_t *const*qq = &qtmp;
		ret = WebRtcAgc_Process(agcInst, pp, 1, samples, qq, inMicLevel,
								outMicLevel, echo, saturationWarning);
		if (ret != 0) {
			printf("%s, line %d : WebRtcAgc_Process1 error\n", __func__, __LINE__);
			return -1;
		}
	}
	return 0;
}

int IMP_AI_EnableNs(IMPAudioIOAttr *attr, int mode)
{
	int ret = -1;
	if ((mode > 3) || (mode < 0)) {
		IMP_LOG_WARN(TAG, "mode too big, should be 0 <= mode <= 3. use default mode 3.\n");
		mode = 3;
	}
	IMP_LOG_INFO(TAG, "AI NS ENABLE: mode = %d\n", mode);

	pthread_mutex_lock(&ns_mutex);
	if (1 == enable_noise_suppression) {
		IMP_LOG_WARN(TAG, "AI NS already enable\n");
		return 0;
	}
	/* char * version = (char *)malloc(sizeof(char) * 20); */
	/* if (version != NULL) { */
	/* 	ret = WebRtcNs_get_version(version, 20); */
	/* 	IMP_LOG_INFO(TAG, "NS version is: %s\n", version); */
	/* 	free(version); */
	/* } */
	handle_ns.handle = WebRtcNs_Create();
	if (handle_ns.handle == 0) {
		IMP_LOG_ERR(TAG, "ns_create error.\n");
		goto err_ns_create;
	}
	ret = WebRtcNs_Init(handle_ns.handle, attr->samplerate);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "Ns_Init error.\n");
		goto err_ns_init;
	}
	ret = WebRtcNs_set_policy(handle_ns.handle, mode);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "Ns_set_policy error.\n");
		goto err_ns_set;
	}

	if (8000 == attr->samplerate) {
		handle_ns.cell_num = 80;
	} else if ((16000 == attr->samplerate)||(32000 == attr->samplerate)||(48000 == attr->samplerate)) {
		handle_ns.cell_num = 160;
	} else {
		IMP_LOG_ERR(TAG, "NS do not supoort samplerate: %d\n", attr->samplerate);
		goto err_ns_err_samplerate;
	}

	handle_ns.sp = malloc(handle_ns.cell_num*sizeof(float));
	if (NULL == handle_ns.sp) {
		IMP_LOG_ERR(TAG, "Ns_sp_malloc error.\n");
		goto err_ns_sp_malloc;
	}
	memset(handle_ns.sp, 0, handle_ns.cell_num*sizeof(float));
	handle_ns.out = malloc(handle_ns.cell_num*sizeof(float));
	if (NULL == handle_ns.out) {
		IMP_LOG_ERR(TAG, "Ns_out_malloc error.\n");
		goto err_ns_out_malloc;
	}
	memset(handle_ns.out, 0, handle_ns.cell_num*sizeof(float));

	enable_noise_suppression = 1;
	pthread_mutex_unlock(&ns_mutex);
	return 0;

err_ns_out_malloc:
err_ns_sp_malloc:
err_ns_err_samplerate:
err_ns_set:
err_ns_init:
err_ns_create:
	pthread_mutex_unlock(&ns_mutex);
	return -1;

}
#if 1
static int Filter(void * filter, short* data, int length) {
  FilterState* hpf = (FilterState *)filter;
  if (hpf == NULL) {
	printf("hpf is NULL\n");
	return -1;
  }

  int tmp_int32 = 0;
  short* y = hpf->y;
  short* x = hpf->x;
  const short* ba = hpf->ba;

  int i = 0;
  for (i = 0; i < length; i++) {
    //  y[i] = b[0] * x[i] + b[1] * x[i-1] + b[2] * x[i-2]
    //         + -a[1] * y[i-1] + -a[2] * y[i-2];

    tmp_int32 =
        WEBRTC_SPL_MUL_16_16(y[1], ba[3]); // -a[1] * y[i-1] (low part)
    tmp_int32 +=
        WEBRTC_SPL_MUL_16_16(y[3], ba[4]); // -a[2] * y[i-2] (low part)
    tmp_int32 = (tmp_int32 >> 15);
    tmp_int32 +=
        WEBRTC_SPL_MUL_16_16(y[0], ba[3]); // -a[1] * y[i-1] (high part)
    tmp_int32 +=
        WEBRTC_SPL_MUL_16_16(y[2], ba[4]); // -a[2] * y[i-2] (high part)
    tmp_int32 = (tmp_int32 << 1);

    tmp_int32 += WEBRTC_SPL_MUL_16_16(data[i], ba[0]); // b[0]*x[0]
    tmp_int32 += WEBRTC_SPL_MUL_16_16(x[0], ba[1]);    // b[1]*x[i-1]
    tmp_int32 += WEBRTC_SPL_MUL_16_16(x[1], ba[2]);    // b[2]*x[i-2]

    // Update state (input part)
    x[1] = x[0];
    x[0] = data[i];

    // Update state (filtered part)
    y[2] = y[0];
    y[3] = y[1];
    y[0] = (short)(tmp_int32 >> 13);
    y[1] = (short)((tmp_int32 -
        WEBRTC_SPL_LSHIFT_W32((int)(y[0]), 13)) << 2);

    // Rounding in Q12, i.e. add 2^11
    tmp_int32 += 2048;

    // Saturate (to 2^27) so that the HP filtered signal does not overflow
    tmp_int32 = WEBRTC_SPL_SAT((int)(134217727),
                               tmp_int32,
                               (int)(-134217728));

    // Convert back to Q0 and use rounding
    data[i] = (short)(tmp_int32>>12);

  }

  return 0;
}
#endif
#if 1
int IngenicHPF_Filter(void * filter, short* data, int size)
{
	int ret = -1;
	char *p = (char *)data;
	int i = 0, k = 0, samples = 0;
	k = size / 160;
	samples = size / 2 / k;
	/*printf("%s: size = %d, samples = %d, k = %d\n", __func__, size, samples, k);*/

	for (i = 0; i < k; i++) {
		ret = Filter(filter, p + 160*i, samples);
		if (ret != 0) {
			printf("%s: Filter error\n", __func__);
			return -1;
		}
	}

	return 0;
}
#endif

int IMP_AI_DisableNs(void)
{
	IMP_LOG_INFO(TAG, "AI NS DISABLE\n");
	pthread_mutex_lock(&ns_mutex);
	enable_noise_suppression = 0;
	WebRtcNs_Free(handle_ns.handle);
	if (NULL != handle_ns.sp) {
		free(handle_ns.sp);
	}
	if (NULL != handle_ns.out) {
		free(handle_ns.out);
	}
	pthread_mutex_unlock(&ns_mutex);
	return 0;
}

static int enable_ai_agc_suppression = 0;
typedef void Handle_agc;
Handle_agc *handle_ai_agc = NULL;
uint32_t agc_sample_i = 0;
int IMP_AI_EnableAgc(IMPAudioIOAttr *attr, IMPAudioAgcConfig agcConfig)
{
	int ret = -1;
	WebRtcAgcConfig config;
	config.targetLevelDbfs = agcConfig.TargetLevelDbfs;
	config.compressionGaindB = agcConfig.CompressionGaindB;
	config.limiterEnable = 1;
	IMP_LOG_INFO(TAG, "AI AGC ENABLE: targetLevelDbfs = %d, compressionGaindB = %d, limiterEnable = %d\n",
			config.targetLevelDbfs, config.compressionGaindB, config.limiterEnable);
	/* char * version = (char *)malloc(sizeof(char) * 20); */
	/* if (version != NULL) { */
	/* 	ret = WebRtcAgc_Version(version, 20); */
	/* 	IMP_LOG_INFO(TAG, "AGC version is: %s\n", version); */
	/* 	free(version); */
	/* } */
	handle_ai_agc = WebRtcAgc_Create();
	if (handle_ai_agc == NULL) {
		IMP_LOG_ERR(TAG, "WebRtcAgc_Create error.\n");
		return -1;
	}

	ret = WebRtcAgc_Init(handle_ai_agc, 0, 255, kAgcModeAdaptiveDigital, attr->samplerate);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "WebRtcAgc_Init error.\n");
		return -1;
	}

	if (8000 == attr->samplerate) {
		agc_sample_i = 80;
	} else if ((16000 == attr->samplerate)||(32000 == attr->samplerate)||(48000 == attr->samplerate)) {
		agc_sample_i = 160;
	} else {
		IMP_LOG_ERR(TAG, "WebRtcAgc do not supoort samplerate: %d\n", attr->samplerate);
		return -1;
	}

	ret = WebRtcAgc_set_config(handle_ai_agc, config);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "WebRtcAgc_set_config error.\n");
		return -1;
	}

	enable_ai_agc_suppression = 1;

	return 0;
}

int IMP_AI_DisableAgc(void)
{
	IMP_LOG_INFO(TAG, "AI AGC DISABLE\n");
	enable_ai_agc_suppression = 0;
	WebRtcAgc_Free(handle_ai_agc);
	return 0;
}

static int enable_ao_agc_suppression = 0;
typedef void Handle_agc;
Handle_agc *handle_ao_agc = NULL;
uint32_t agc_sample_o = 0;
int IMP_AO_EnableAgc(IMPAudioIOAttr *attr, IMPAudioAgcConfig agcConfig)
{
	int ret = -1;
	WebRtcAgcConfig config;
	config.targetLevelDbfs = agcConfig.TargetLevelDbfs;
	config.compressionGaindB = agcConfig.CompressionGaindB;
	config.limiterEnable = 1;
	IMP_LOG_INFO(TAG, "AO AGC ENABLE: targetLevelDbfs = %d, compressionGaindB = %d, limiterEnable = %d\n",
			config.targetLevelDbfs, config.compressionGaindB, config.limiterEnable);
	/* printf("samplerate = %d, bitwidth = %d, soundmode = %d, frmNum = %d, numPerFrm = %d, chnCnt = %d\n", */
	/* 		attr->samplerate, attr->bitwidth, attr->soundmode, attr->frmNum, attr->numPerFrm, attr->chnCnt); */

	/* char * version = (char *)malloc(sizeof(char) * 20); */
	/* if (version != NULL) { */
	/* 	ret = WebRtcAgc_Version(version, 20); */
	/* 	IMP_LOG_INFO(TAG, "AGC version is: %s\n", version); */
	/* 	free(version); */
	/* } */
	//ret = WebRtcAgc_Create(&handle_ao_agc);
	handle_ao_agc = WebRtcAgc_Create();
	if (handle_ao_agc == NULL) {
		IMP_LOG_ERR(TAG, "WebRtcAgc_Create error.\n");
		return -1;
	}

	ret = WebRtcAgc_Init(handle_ao_agc, 0, 255, kAgcModeAdaptiveDigital, attr->samplerate);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "WebRtcAgc_Init error.\n");
		return -1;
	}
	if (8000 == attr->samplerate) {
		agc_sample_o = 80;
	} else if ((16000 == attr->samplerate)||(32000 == attr->samplerate)||(48000 == attr->samplerate)) {
		agc_sample_o = 160;
	} else {
		IMP_LOG_ERR(TAG, "WebRtcAgc do not supoort samplerate: %d\n", attr->samplerate);
		return -1;
	}

	ret = WebRtcAgc_set_config(handle_ao_agc, config);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "WebRtcAgc_set_config error.\n");
		return -1;
	}

	enable_ao_agc_suppression = 1;

	return 0;
}

int IMP_AO_DisableAgc(void)
{
	IMP_LOG_INFO(TAG, "AO AGC DISABLE\n");
	enable_ao_agc_suppression = 0;
	WebRtcAgc_Free(handle_ao_agc);
	return 0;
}

static int enable_ai_hpf_suppression = 0;
FilterState handle_ai_hpf;

const int16_t kFilterCoefficients8kHz[5] =
{3798, -7596, 3798, 7807, -3733};//bk
/* {3875, -7750, 3875, 7737, -3665}; *///100
/* {3768, -7536, 3768, 7511, -3467};//150*/
/* {3665, -7330, 3665, 7285, -3280};//200*/
/* {3565, -7130, 3565, 7061, -3103};//250 */
/* {3279, -6558, 3279, 6394, -2627};//400*/
/* {3101, -6202, 3101, 5957, -2351};//500 */
/* {2331, -4662, 2331, 3862, -1365};//1000 */

const int16_t kFilterCoefficients[5] =
/* {3096, -6192, 3096, 2972, -1172};//bk*/
{3665, -7330, 3665, 7285, -3280};//400
/* {3101, -6202, 3101, 5956, -2351};//1000 */
/* {2617, -5234, 2617, 2341, -846}; */
/* {4012, -8024, 4012, 8002, -3913}; */

#define kSampleRate8kHz		8000
int InitializeFilter(void * filter, int sample_rate_hz)
{
	FilterState* hpf = (FilterState *)filter;
	if (hpf == NULL) {
		printf("hpf is NULL\n");
		return -1;
	}

	if (sample_rate_hz == kSampleRate8kHz) {
		hpf->ba = kFilterCoefficients8kHz;
	} else {
		hpf->ba = kFilterCoefficients;
	}

	WebRtcSpl_MemSetW16(hpf->x, 0, 2);
	WebRtcSpl_MemSetW16(hpf->y, 0, 4);

	return 0;
}

int IMP_AI_EnableHpf(IMPAudioIOAttr *attr)
{
	int ret = -1;
	char * version = (char *)malloc(sizeof(char) * 32);
	if (version != NULL) {
		ret = Hpf_Version(version, 32);
		IMP_LOG_INFO(TAG, "HPF version is: %s\n", version);
		free(version);
	}
	ret = InitializeFilter(&handle_ai_hpf, attr->samplerate);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "HPF InitializeFilter error.\n");
		return -1;
	}

	enable_ai_hpf_suppression = 1;

	return 0;
}

int IMP_AI_DisableHpf(void)
{
	IMP_LOG_INFO(TAG, "AI HPF DISABLE\n");
	enable_ai_hpf_suppression = 0;
	return 0;
}

static int enable_ao_hpf_suppression = 0;
FilterState handle_ao_hpf;
int IMP_AO_EnableHpf(IMPAudioIOAttr *attr)
{
	int ret = -1;
	char * version = (char *)malloc(sizeof(char) * 32);
	if (version != NULL) {
		ret = Hpf_Version(version, 32);
		IMP_LOG_INFO(TAG, "HPF version is: %s\n", version);
		free(version);
	}
	ret = InitializeFilter(&handle_ao_hpf, attr->samplerate);
	if (ret != 0) {
		IMP_LOG_ERR(TAG, "HPF InitializeFilter error.\n");
		return -1;
	}

	enable_ao_hpf_suppression = 1;

	return 0;
}

int IMP_AO_DisableHpf(void)
{
	IMP_LOG_INFO(TAG, "AO HPF DISABLE\n");
	enable_ao_hpf_suppression = 0;
	return 0;
}

#define MAX_16BIT 32767
#define MIN_16BIT -32767
void Audio_Set_Volume(char *src, char *dst, int size, int pcmbit, double exv)
{
	unsigned char temp8;
	short temp16;
	int temp32;
	unsigned int i;

	switch (pcmbit) {
		case 16:
			{
				short *psrc = (short *)src;
				short *pdst = (short *)dst;

				for (i = 0; i < size/2; i++) {
					temp16 = psrc[i];
					temp16 = (double)temp16 * exv;
#if 1
					temp32 =  psrc[i] * exv;
					if (temp32 > MAX_16BIT)
						temp16 = MAX_16BIT;
					else if (temp32 < MIN_16BIT)
						temp16 = MIN_16BIT;
#endif
#if 0
					if (exv >= 0)
						temp16 = psrc[i] << exv;
					else
						temp16 = psrc[i] >> abs(exv);
					if (temp16 > MAX_16BIT)
						temp16 = MAX_16BIT;
					else if (temp16 < MIN_16BIT)
						temp16 = MIN_16BIT;
#endif
					pdst[i] = temp16;
				}
			}
			break;
		case 8:
			for (i = 0; i < size; i++)
			{
				temp8 = src[i];
				temp8 = (double)temp8 * exv;

#if 0
				if (exv >= 0)
					temp8 = temp8 << exv;
				else
					temp8 = temp8 >> abs(exv);
#endif
				dst[i] = temp8;
			}
			break;
		default:
			IMP_LOG_ERR(TAG, "error pcmbit \n");
	}
}

static uint64_t Audio_Get_Time_Stamp()
{
	return IMP_System_GetTimeStamp();
}

inline void Chn_Lock(pthread_mutex_t *mutex)
{
	pthread_mutex_lock(mutex);
}

inline void Chn_UnLock(pthread_mutex_t *mutex)
{
	pthread_mutex_unlock(mutex);
}

static void Audio_Play_Wait()
{
	struct timeval now;
	struct timespec timeout;

	pthread_mutex_lock(&play_mutex);
	audio_play_is_ready = 1;
	gettimeofday(&now, NULL);
	timeout.tv_sec = now.tv_sec + 5;
	timeout.tv_nsec = now.tv_usec * 1000;
	if(pthread_cond_timedwait(&play_cond, &play_mutex, &timeout) == ETIMEDOUT)
		IMP_LOG_WARN(TAG, "Audio Play Wait timeout\n");
	pthread_mutex_unlock(&play_mutex);
}

static void Audio_Play_Post()
{
	pthread_mutex_lock(&play_mutex);
	audio_play_is_ready = 0;
	pthread_cond_signal(&play_cond);
	pthread_mutex_unlock(&play_mutex);
}

static int Audio_Record_Wait(unsigned int time_ms)
{
	struct timeval now;
	struct timespec timeout;

	pthread_mutex_lock(&record_mutex);
	audio_record_is_ready = 1;
	gettimeofday(&now, NULL);
	if ((now.tv_usec + (time_ms % 1000) * 1000) / 1000000 > 0)
		now.tv_sec += 1;

	timeout.tv_sec = now.tv_sec + (time_ms / 1000);
	timeout.tv_nsec = ((now.tv_usec + (time_ms % 1000) * 1000) % 1000000) * 1000;
	if(pthread_cond_timedwait(&record_cond, &record_mutex, &timeout) == ETIMEDOUT) {
		IMP_LOG_WARN(TAG, "Audio Record Wait timeout\n");
		pthread_mutex_unlock(&record_mutex);
		return -1;
	}
	pthread_mutex_unlock(&record_mutex);
	return 0;
}

static void Audio_Record_Post()
{
	pthread_mutex_lock(&record_mutex);
	audio_record_is_ready = 0;
	pthread_cond_signal(&record_cond);
	pthread_mutex_unlock(&record_mutex);
}

static inline int Audio_Record_Play_is_ready()
{
	return (audio_record_is_ready && audio_play_is_ready);
}

static inline int Audio_Record_is_ready()
{
	return (audio_record_is_ready);
}

static inline int Audio_Play_is_ready()
{
	return (audio_play_is_ready);
}

static void Audio_Lock()
{
	pthread_mutex_lock(&mutex);
}

static void Audio_Unlock()
{
	pthread_mutex_unlock(&mutex);
}

AudioList *Audio_List_Alloc(AudioListMode mode, int max_buf_num)
{
	AudioList *head = NULL;
	AudioList *tmp_p = NULL;
	AudioList *tmp_n = NULL;
	int num = 1; /* head is alredy alloc */

	head = (AudioList *)malloc(sizeof(AudioList));
	if(head == NULL) {
		IMP_LOG_ERR(TAG, "alloc AudioList head failed\n");
		return NULL;
	}
	memset(head, 0x0, sizeof(AudioList));

	head->next = NULL;
	head->mode = mode;
	head->used = LIST_NOT_USED;
	head->list_num = 0;
	head->audio_buf = malloc(MAX_AUDIO_BUF_SIZE);
	if(head->audio_buf == NULL) {
		IMP_LOG_ERR(TAG, "alloc head audio buf failed\n");
		return NULL;
	}

	tmp_p = head;

	for(num = 1; num < max_buf_num; num++) {
		tmp_n = (AudioList *)malloc(sizeof(AudioList));
		if(tmp_n == NULL) {
			IMP_LOG_ERR(TAG, "alloc AudioList failed\n");
			return NULL;
		}
		memset(tmp_n, 0x0, sizeof(AudioList));

		tmp_n->next = NULL;
		tmp_n->mode = mode;
		tmp_n->used = LIST_NOT_USED;
		tmp_n->list_num = num;
		tmp_n->audio_buf = malloc(MAX_AUDIO_BUF_SIZE);
		if(tmp_n->audio_buf == NULL) {
			IMP_LOG_ERR(TAG, "alloc audio buf failed\n");
			return NULL;
		}

		tmp_p->next = tmp_n;
		tmp_p = tmp_n;
	}

	tmp_n->next = head;
	return head;
}

int Audio_List_Free(AudioList *list)
{
	AudioList *tmp = list;
	if(list == NULL) {
		IMP_LOG_ERR(TAG, "Audio_List_Free list is NULL\n");
		return -1;
	}

	while(1) {
		AudioList *free_tmp = tmp;
		if(tmp->audio_buf) {
			free(tmp->audio_buf);
			tmp->audio_buf = NULL;
		}

		tmp = tmp->next;
		free(free_tmp);

		if(tmp == list)
			break;
	}

	list = NULL;
	return 0;
}

static int Audio_List_Init(void)
{
	Audio_Lock();
	audio_list_init_ref_num++;
	if(audio_list_init_ref_num > 1) {
		Audio_Unlock();
		return 0;
	}

	if(g_record_head_in == NULL) {
		g_record_head_in = Audio_List_Alloc(MODE_RECORD, MAX_AUDIO_BUF_NUM);
		if(g_record_head_in == NULL) {
			IMP_LOG_ERR(TAG, "record head list alloc error\n");
			Audio_Unlock();
			return -1;
		}
		g_record_head_out = g_record_head_in;
	}

	if(g_play_head_in == NULL) {
		g_play_head_in = Audio_List_Alloc(MODE_PLAY, MAX_AUDIO_BUF_NUM);
		if(g_play_head_in == NULL) {
			IMP_LOG_ERR(TAG, "record head list alloc error\n");
			Audio_Unlock();
			return -1;
		}
		g_play_head_out = g_play_head_in;
	}

	if(g_filter_head_in == NULL) {
		g_filter_head_in = Audio_List_Alloc(MODE_FILTER, MAX_AUDIO_BUF_NUM);
		if(g_filter_head_in == NULL) {
			IMP_LOG_ERR(TAG, "record head list alloc error\n");
			Audio_Unlock();
			return -1;
		}
		g_filter_head_out = g_filter_head_in;
	}

	Audio_Unlock();
	return 0;
}

static int Audio_List_DeInit(void)
{
	Audio_Lock();

	audio_list_init_ref_num--;
	if(audio_list_init_ref_num > 0) {
		Audio_Unlock();
		return 0;
	}

	if(g_record_head_in != NULL) {
		if(Audio_List_Free(g_record_head_in)) {
			IMP_LOG_ERR(TAG, "Audio list Deinit (free record list) failed\n");
			Audio_Unlock();
			return -1;
		}
		g_record_head_in = NULL;
		g_record_head_out = NULL;
	}

	if(g_play_head_in != NULL) {
		if(Audio_List_Free(g_play_head_in)) {
			IMP_LOG_ERR(TAG, "Audio list Deinit (free play list) failed\n");
			Audio_Unlock();
			return -1;
		}
		g_play_head_in = NULL;
		g_play_head_out = NULL;
	}

	if(g_filter_head_in != NULL) {
		if(Audio_List_Free(g_filter_head_in)) {
			IMP_LOG_ERR(TAG, "Audio list Deinit (free filter list) failed\n");
			Audio_Unlock();
			return -1;
		}
		g_filter_head_in = NULL;
		g_filter_head_out = NULL;
	}

	Audio_Unlock();
	return 0;
}

AudioList *Audio_Prefetch_List(AudioListMode mode, AudioListUsed used)
{
	AudioList *list = NULL;

	Audio_Lock();
	if(mode == MODE_RECORD) {
		if((g_record_head_in != NULL) && (g_record_head_out != NULL)) {
			if(used == LIST_NOT_USED) {
				if(g_record_head_in->used == LIST_NOT_USED) {
					list = g_record_head_in;
				}
			} else if(used == LIST_USED) {
				if(g_record_head_out->used == LIST_USED) {
					list = g_record_head_out;
				}
			} else {
				IMP_LOG_ERR(TAG, "Audio_Prefetch_List is not support Audio list used\n");
			}
		}
	} else if(mode == MODE_PLAY) {
		if((g_play_head_in != NULL) && (g_play_head_out != NULL)) {
			if(used == LIST_NOT_USED) {
				if(g_play_head_in->used == LIST_NOT_USED) {
					list = g_play_head_in;
				}
			} else if(used == LIST_USED) {
				if(g_play_head_out->used == LIST_USED) {
					list = g_play_head_out;
				}
			} else {
				IMP_LOG_ERR(TAG, "Audio_Prefetch_List is not support Audio list used\n");
			}
		}
	} else if(mode == MODE_FILTER) {
		if((g_filter_head_in != NULL) && (g_filter_head_out != NULL)) {
			if(used == LIST_NOT_USED) {
				if(g_filter_head_in->used == LIST_NOT_USED) {
					list = g_filter_head_in;
				}
			} else if(used == LIST_USED) {
				if(g_filter_head_out->used == LIST_USED) {
					list = g_filter_head_out;
				}
			} else {
				IMP_LOG_ERR(TAG, "Audio_Prefetch_List is not support Audio list used\n");
			}
		}
	} else {
		IMP_LOG_ERR(TAG, "Audio_Prefetch_List is not support Audio list mode\n");
	}

	Audio_Unlock();

	// For Debug
	/*
	if(list) {
		printf("[DEBUG-Prefetch]: list->mode = %s, list->used = %d, list->list_num = %d, list->size = %d\n",
				list->mode == MODE_RECORD? "Record" : (list->mode == MODE_PLAY ? "Play" : "Filter"),
				list->used, list->list_num, list->buf_size);
	}
	*/

	return list;
}

AudioList *Audio_Chn_Prefetch_List(AudioList **in, AudioList **out, AudioListUsed used)
{
	AudioList *list = NULL;
	AudioList *tmp_in = (AudioList *)*in;
	AudioList *tmp_out = (AudioList *)*out;

	if((tmp_in != NULL) && (tmp_out != NULL)) {
		if(used == LIST_NOT_USED) {
			if(tmp_in->used == LIST_NOT_USED) {
				list = tmp_in;
			}
		} else if(used == LIST_USED) {
			if(tmp_out->used == LIST_USED) {
				list = tmp_out;
			}
		} else {
			IMP_LOG_ERR(TAG, "Audio_Chn_Prefetch_List is not support Audio list used\n");
		}
	}
	return list;
}

static int Audio_Release_List(AudioListMode mode, AudioListUsed used)
{
	/*AudioList *list = NULL;*/
	Audio_Lock();
	if(mode == MODE_RECORD) {
		if((g_record_head_in != NULL) && (g_record_head_out != NULL)) {
			if(used == LIST_NOT_USED) {
				if(g_record_head_in->used == LIST_NOT_USED) {
					g_record_head_in->used = LIST_USED;
					/*list = g_record_head_in;*/
					g_record_head_in = g_record_head_in->next;
				}
			} else if(used == LIST_USED) {
				if(g_record_head_out->used == LIST_USED) {
					g_record_head_out->used = LIST_NOT_USED;
					g_record_head_out->buf_size = 0;
					/*list = g_record_head_out;*/
					g_record_head_out = g_record_head_out->next;
				}
			} else {
				IMP_LOG_ERR(TAG, "Audio_Release_List is not support Audio list used\n");
			}
		}
	} else if(mode == MODE_PLAY) {
		if((g_play_head_in != NULL) && (g_play_head_out != NULL)) {
			if(used == LIST_NOT_USED) {
				if(g_play_head_in->used == LIST_NOT_USED) {
					g_play_head_in->used = LIST_USED;
					/*list = g_play_head_in;*/
					g_play_head_in = g_play_head_in->next;
				}
			} else if(used == LIST_USED) {
				if(g_play_head_out->used == LIST_USED) {
					g_play_head_out->used = LIST_NOT_USED;
					g_play_head_out->buf_size = 0;
					/*list = g_play_head_out;*/
					g_play_head_out = g_play_head_out->next;
				}
			} else {
				IMP_LOG_ERR(TAG, "Audio_Release_List is not support Audio list used\n");
			} }
	} else if(mode == MODE_FILTER) {
		if((g_filter_head_in != NULL) && (g_filter_head_out != NULL)) {
			if(used == LIST_NOT_USED) {
				if(g_filter_head_in->used == LIST_NOT_USED) {
					g_filter_head_in->used = LIST_USED;
					/*list = g_filter_head_in;*/
					g_filter_head_in = g_filter_head_in->next;
				}
			} else if(used == LIST_USED) {
				if(g_filter_head_out->used == LIST_USED) {
					g_filter_head_out->used = LIST_NOT_USED;
					g_filter_head_out->buf_size = 0;
					/*list = g_filter_head_out;*/
					g_filter_head_out = g_filter_head_out->next;
				}
			} else {
				IMP_LOG_ERR(TAG, "Audio_Release_List is not support Audio list used\n");
			}
		}
	} else {
		IMP_LOG_ERR(TAG, "Audio_Release_List is not support Audio list mode\n");
	}

	Audio_Unlock();

	// For Debug
	/*
	if(list) {
		printf("[DEBUG-Release]: list->mode = %s, list->used = %d, list->list_num = %d, list->size = %d\n",
				list->mode == MODE_RECORD? "Record" : (list->mode == MODE_PLAY ? "Play" : "Filter"),
				list->used, list->list_num, list->buf_size);
	}
	*/

	return 0;
}

int Audio_Chn_Release_List(AudioList **in, AudioList **out, AudioListUsed used)
{
	AudioList *tmp_in = (AudioList *)*in;
	AudioList *tmp_out = (AudioList *)*out;

	if((tmp_in != NULL) && (tmp_out != NULL)) {
		if(used == LIST_NOT_USED) {
			if(tmp_in->used == LIST_NOT_USED) {
				tmp_in->used = LIST_USED;
				*in = tmp_in->next;
			}
		} else if(used == LIST_USED) {
			if(tmp_out->used == LIST_USED) {
				tmp_out->used = LIST_NOT_USED;
				tmp_out->buf_size = 0;
				*out = tmp_out->next;
			}
		} else {
			IMP_LOG_ERR(TAG, "Audio_Release_List is not support Audio list used\n");
		}
	}
	return 0;
}

static int Audio_Clear_List(AudioListMode mode)
{
	Audio_Lock();
	if(mode == MODE_RECORD) {
		if(g_record_head_in && g_record_head_out) {
			while(1) {
				g_record_head_out->used = LIST_NOT_USED;
				g_record_head_out = g_record_head_out->next;
				if(g_record_head_out == g_record_head_in)
					break;
			}
		}
	} else if(mode == MODE_PLAY) {
		if(g_play_head_in && g_play_head_out) {
			while(1) {
				g_play_head_out->used = LIST_NOT_USED;
				g_play_head_out = g_play_head_out->next;
				if(g_play_head_out == g_play_head_in)
					break;
			}
		}
	} else if(mode == MODE_FILTER) {
		if(g_filter_head_in && g_filter_head_out) {
			while(1) {
				g_filter_head_out->used = LIST_NOT_USED;
				g_filter_head_out = g_filter_head_out->next;
				if(g_filter_head_out == g_filter_head_in)
					break;
			}
		}
	} else {
		IMP_LOG_ERR(TAG, "Audio_Clear_List is not support Audio list mode\n");
		Audio_Unlock();
		return -1;
	}

	Audio_Unlock();
	return 0;
}

int Audio_Chn_Clear_List(AudioList **in, AudioList **out)
{
	AudioList *tmp_in = (AudioList *)*in;
	AudioList *tmp_out = (AudioList *)*out;

	if(tmp_in && tmp_out) {
		while(1) {
			tmp_out->used = LIST_NOT_USED;
			tmp_out = tmp_out->next;
			if(tmp_out == tmp_in)
				break;
		}
	}

	*out = *in;
	return 0;
}

static inline void Audio_Echo_Suppressor_Start()
{
	Audio_Lock();
	echo_suppressor_start = 1;
	Audio_Unlock();
}

static inline int Audio_Echo_Suppressor_Is_Start()
{
	return echo_suppressor_start;
}

static inline void Audio_Echo_Suppressor_Cleared()
{
	echo_suppressor_clear = 1;
}

static inline int Audio_Echo_Suppressor_Is_Clear()
{
	return echo_suppressor_clear;
}

/**
 * Audio Echo Suppressor interface
 */
int IMP_Audio_GetEchoSuppressorState()
{
	return enable_echo_suppressor;
}

int IMP_Audio_DisableEchoSuppressor()
{
	IMP_LOG_INFO(TAG, "IMP_Audio_DisableEchoSuppressor \n");

	if(!IMP_Audio_GetEchoSuppressorState())
		return 0;

	Audio_Clear_List(MODE_RECORD);
	Audio_Clear_List(MODE_PLAY);
	Audio_Clear_List(MODE_FILTER);

	pthread_mutex_lock(&aec_mutex);
	//Audio_Lock();
	enable_echo_suppressor = 0;
	echo_suppressor_start = 0;
	echo_suppressor_clear = 0;
	echo_time_sync = 0;
	ingenic_apm_destroy();
	//Audio_Unlock();

	Audio_Play_Post();
	Audio_Record_Post();
	pthread_mutex_unlock(&aec_mutex);
	return 0;
}

int IMP_Audio_EnableEchoSuppressor(int devId)
{
	if(IMP_Audio_GetEchoSuppressorState()){
	    IMP_Audio_DisableEchoSuppressor();
	    usleep(500000);
	}

	IMP_LOG_INFO(TAG, "IMP_Audio_EnableEchoSuppressor \n");

	echo_time_sync = 1;
	Audio_Lock();
	enable_echo_suppressor = 1;
	ingenic_apm_init(audioDev[devId].audioIAttr.samplerate);
	Audio_Unlock();
	return 0;
}

/**
 * Audio Record interface
 */
#define DMIC_NAME	"/dev/jz-dmic"
#define AMIC_NAME	"/dev/dsp"
/**
 *  DMIC_SET_SAMPLERATE IS FOLLOW THE DRIVER
 **/
#define DMIC_SET_SAMPLERATE		0x103
/**
 *  DMIC JUST SUPPORT 8K & 16K SAMPLERATE,
 *  DEFAULT SAMPLERATE IS 16K.
 **/
#define DMIC_SAMPLERATE		AUDIO_SAMPLE_RATE_8000

static int dmic_fd = -1;
static void *amic_fd_client = NULL;
static int amic_fd = -1;

/**
 * 录音MIC选择.
 * @remarks 根据板子硬件设计而定,如果硬件接有两种MIC,那么可以进行选择,如果只有一种,那么就固定一个选择即可.
 */
typedef enum {
	AUDIO_MIC_D,		/**< 数字MIC */
	AUDIO_MIC_A,		/**< 模拟MIC */
} SUAudioMicIndex;

/**
 * 放音SPK选择.
 * @remarks 根据板子硬件设计而定,如果硬件只接有一个喇叭,那么选择默认喇叭即可,如果硬件接有两个喇叭,那么就可以根据喇叭Index进行选择.
 */
typedef enum {
	AUDIO_SPK_DEFAULT,	/**< 默认喇叭 */
	AUDIO_SPK_BACK,	/**< 其他喇叭 */
} SUAudioSpkIndex;

/**
 * 录音参数结构体.
 * @remarks 在录音时,结构体中的id,buf地址,max_size需要赋值后作为输入参数.buf中的内容,timestamp作为输出参数.
 */
typedef struct {
	SUAudioMicIndex id;	/**< 数字MIC或者模拟MIC选择 */
	char *buf;			/**< 录音数据存放缓存指针 */
	int max_size;		/**< 录音数据存放缓存大小 */
	struct timeval timestamp;	/**< 当前录音数据时间戳 */
} SUAudioRecordAttr;

/**
 * 放音参数结构体.
 */
typedef struct {
	SUAudioSpkIndex id;	/**< 选择放音喇叭 */
	char *buf;			/**< 放音数据存放缓存指针 */
	int bytes;		/**< 放音数据大小 */
} SUAudioPlayAttr;


int audioin_dev_ref_enable(int aiDevId)
{
	int i = 0;
	for(i = 0; i < MAX_AUDIO_IN_CHN; i++) {
		if(1 == audioDev[aiDevId].audioInChn[i].enable_ref) {
			return 1;
		}
	}
	return 0;
}

int audioin_dev_chn_ref_enable(int aiDevId, int aiChn)
{
	if(1 == audioDev[aiDevId].audioInChn[aiChn].enable_ref) {
		return 1;
	}
	else {
		return 0;
	}
}

int audioin_ref_enable(void)
{
	int i = 0;
	for(i = 0; i < MAX_AUDIO_DEV; i++) {
		if (1 == audioDev[i].aiEnable)
			if(1 == (audioin_dev_ref_enable(i))) {
				return 1;
			}
	}
	return 0;
}

static void *Audio_Record_Thread(void *argv)
{
	int ret = -1;
	int file_fd = -1;
	void *file_fd_client = NULL;
	int outMicLevel = 0;
	unsigned char saturationWarning = 0;

	SUAudioMicIndex id = *((SUAudioMicIndex *)argv);

	char thread_name[64];
	sprintf(thread_name, "Audio-%s", __func__);
	prctl(PR_SET_NAME, (unsigned long)thread_name);


	if (id == AUDIO_MIC_D) {/*DIGITAL MIC*/
		if (dmic_fd < 0) {
			IMP_LOG_ERR(TAG, "dmic_fd is not opened, please exec IMP_Audio_RecordInit function is before Record.\n");
			return NULL;
		}
		file_fd = dmic_fd;
	} else if (id == AUDIO_MIC_A) {/*ANALOG MIC*/
		if (1 == audio_client) {
			if (amic_fd_client == 0) {
				IMP_LOG_ERR(TAG, "amic_fd is not opened, please exec IMP_Audio_RecordInit function is before Record.\n");
				return NULL;
			}
			file_fd_client = amic_fd_client;
		} else {
			if (amic_fd < 0) {
				IMP_LOG_ERR(TAG, "amic_fd is not opened, please exec IMP_Audio_RecordInit function is before Record.\n");
				return NULL;
			}
			file_fd = amic_fd;
		}
	} else {
		IMP_LOG_ERR(TAG, "Audio MIC index is not support this id(%d)\n", id);
		return NULL;
	}

#ifdef AUDIO_DEBUG
	FILE * file_record = fopen("/tmp/record.pcm", "wb");
	FILE * file_play = fopen("/tmp/play.pcm", "wb");
	FILE * file_filter = fopen("/tmp/filter.pcm", "wb");
#endif

	while(audio_record_thread_run) {
		AudioList *list = Audio_Prefetch_List(MODE_RECORD, LIST_NOT_USED);
		if(list == NULL) {
			Audio_Release_List(MODE_RECORD, LIST_USED);
			continue;
		}

		if(IMP_Audio_GetEchoSuppressorState() || audioin_dev_ref_enable(id)) {
			if(!Audio_Echo_Suppressor_Is_Clear() && Audio_Play_is_ready()) {
				int clear_num = 100;
				struct timeval now1;
				struct timeval now2;
				int time_interval = 0;

				while(clear_num--) {
					char clear_buf[MAX_AUDIO_BUF_SIZE];
					int size = 0;
					gettimeofday(&now1, NULL);
					if (1 == audio_client) {
						size = audioin_read(amic_fd_client, clear_buf, MAX_AUDIO_BUF_SIZE);
					} else {
						size = read(file_fd, clear_buf, MAX_AUDIO_BUF_SIZE);
					}
					gettimeofday(&now2, NULL);
					if(now1.tv_usec < now2.tv_usec) {
					    time_interval = now2.tv_usec - now1.tv_usec;
					} else {
					    time_interval = (now2.tv_sec - now1.tv_sec) * 1000000 + now2.tv_usec - now1.tv_usec;
					}
					if(time_interval > 1000) {
					    break;
					}
					if(size < 0) {
						IMP_LOG_ERR(TAG, "read record data error\n");
						break;
					}
				}
				Audio_Clear_List(MODE_RECORD);
				Audio_Clear_List(MODE_PLAY);
				Audio_Clear_List(MODE_FILTER);
				Audio_Echo_Suppressor_Cleared();
			}
			if(Audio_Play_is_ready()) {
				Audio_Play_Post();
				usleep(0);
			}
		}

		list->timestamp = Audio_Get_Time_Stamp();
		if (1 == audio_client) {
			if ((list->buf_size = audioin_read(amic_fd_client, list->audio_buf, MAX_AUDIO_BUF_SIZE)) < 0) {
				IMP_LOG_ERR(TAG, "read record data error\n");
				break;
			}
		} else {
			if ((list->buf_size = read(file_fd, list->audio_buf, MAX_AUDIO_BUF_SIZE)) < 0) {
				IMP_LOG_ERR(TAG, "read record data error\n");
				break;
			}
		}
		Audio_Release_List(MODE_RECORD, LIST_NOT_USED);

		pthread_mutex_lock(&aec_mutex);

		if(IMP_Audio_GetEchoSuppressorState() || audioin_dev_ref_enable(id)) {
			/*if((record_sample_rate != play_sample_rate) || (record_sample_rate != SAMPLE_RATE)) {
				printf("ERROR: If Enable Echo Suppressor Function.\n");
				printf("Audio sample rate and playback sample rate must to be the same.\n");
				printf("And Need is (%d)\n", SAMPLE_RATE);
				[>break;<]
			}*/

			AudioList *list_record = Audio_Prefetch_List(MODE_RECORD, LIST_USED);
			if(list_record == NULL) {
				IMP_LOG_ERR(TAG, "ERROR: list record is NULL\n");
				continue;
			}

			AudioList *list_filter = Audio_Prefetch_List(MODE_FILTER, LIST_NOT_USED);
			if(list_filter == NULL) {
				Audio_Release_List(MODE_FILTER, LIST_USED);
				list_filter = Audio_Prefetch_List(MODE_FILTER, LIST_NOT_USED);
				if(list_filter == NULL) {
					IMP_LOG_ERR(TAG, "ERROR: list filter is NULL\n");
					continue;
				}
			}

			AudioList *list_play = Audio_Prefetch_List(MODE_PLAY, LIST_USED);
			if(list_play == NULL) {
				/*
				   list_filter->buf_size = list_record->buf_size;
				   list_filter->timestamp = list_record->timestamp;
				   memcpy(list_filter->audio_buf, list_record->audio_buf, list_record->buf_size);
				   */
				if(audioDev[id].aiEnable) {
					int i = 0;
					for(i = 0; i < MAX_AUDIO_IN_CHN; i++) {
						if(audioDev[id].audioInChn[i].enable == 1) {
#if 1
							pthread_mutex_lock(&ns_mutex);
							if (enable_noise_suppression) {
								ret = IngenicNs_Process(&handle_ns, (short *)list_record->audio_buf, (short *)list_record->audio_buf, list_record->buf_size);
								if (ret != 0) {
									IMP_LOG_ERR(TAG, "IngenicNs_Process error.\n");
									pthread_mutex_unlock(&ns_mutex);
									return NULL;
								}
							}
							pthread_mutex_unlock(&ns_mutex);
#endif
							if (enable_ai_agc_suppression) {
								ret = IngenicAgc_Process(handle_ai_agc, (short *)list_record->audio_buf,
										agc_sample_i, (short *)list_record->audio_buf, 127,
										&outMicLevel, 0, &saturationWarning, list_record->buf_size);
								if (ret != 0) {
									IMP_LOG_ERR(TAG, "IngenicAgc_Process ai error.\n");
									return NULL;
								}
							}
#if 0
							if (enable_ai_hpf_suppression) {
								ret = IngenicHPF_Filter(&handle_ai_hpf, (short *)list_record->audio_buf, list_record->buf_size);
								if (ret != 0) {
									IMP_LOG_ERR(TAG, "IngenicHPF_Filter ai error.\n");
									return NULL;
								}
							}
#endif
							Chn_Lock(&audioDev[id].audioInChn[i].chnLock);
							AudioList *tmp = Audio_Chn_Prefetch_List(&audioDev[id].audioInChn[i].frameHeadIn, &audioDev[id].audioInChn[i].frameHeadOut, LIST_NOT_USED);
							if(tmp) {
								Audio_Set_Volume((char *)list_record->audio_buf, (char *)tmp->audio_buf, list_record->buf_size,
										16, audioDev[id].audioInChn[i].vol.vol);
								tmp->buf_size = list_record->buf_size;
								tmp->timestamp = list_record->timestamp;
								/* ref */
								if (0 != audioin_dev_chn_ref_enable(id, i)) {
									if (NULL != tmp->ref) {
										if (NULL != tmp->ref->audio_buf) {
											//printf("### tmp->ref->audio_buf = %p list_play->buf_size = %d\n",
											//		tmp->ref->audio_buf, list_play->buf_size);
											memset(tmp->ref->audio_buf, 0, list_record->buf_size);
										} else {
											IMP_LOG_ERR(TAG, "ERROR: NULL != tmp->ref->audio_buf\n");
										}
									} else {
										IMP_LOG_ERR(TAG, "ERROR: NULL != tmp->ref\n");
									}
								}
								Audio_Chn_Release_List(&audioDev[id].audioInChn[i].frameHeadIn, &audioDev[id].audioInChn[i].frameHeadOut, LIST_NOT_USED);
							}
							Chn_UnLock(&audioDev[id].audioInChn[i].chnLock);
						}
					}
				}
			} else {
				/* aec */
				if(IMP_Audio_GetEchoSuppressorState()) {
					int i = 0;
					for(i = 0; i < (list_record->buf_size / AUDIO_ECHO_SUPPRESSOR_SIZE); i++) {
						short *far_frame = (short *)(list_play->audio_buf + AUDIO_ECHO_SUPPRESSOR_SIZE * i);
						short *near_frame = (short *)(list_record->audio_buf + AUDIO_ECHO_SUPPRESSOR_SIZE * i);
						short *filter_frame = (short *)(list_filter->audio_buf + AUDIO_ECHO_SUPPRESSOR_SIZE * i);
						ingenic_apm_set_far_frame(far_frame);
						ingenic_apm_set_near_frame(near_frame, filter_frame);
					}
				}
				/* ref */
				if(audioin_dev_ref_enable(id)) {
					memcpy(list_filter->audio_buf, list_record->audio_buf, list_record->buf_size);
				}
				list_filter->buf_size = list_record->buf_size;
				list_filter->timestamp = list_record->timestamp;

				if(audioDev[id].aiEnable) {
					int i = 0;
					for(i = 0; i < MAX_AUDIO_IN_CHN; i++) {
						if(audioDev[id].audioInChn[i].enable == 1) {
							Chn_Lock(&audioDev[id].audioInChn[i].chnLock);
							AudioList *tmp = Audio_Chn_Prefetch_List(&audioDev[id].audioInChn[i].frameHeadIn, &audioDev[id].audioInChn[i].frameHeadOut, LIST_NOT_USED);
							if(tmp) {
								Audio_Set_Volume((char *)list_filter->audio_buf, (char *)tmp->audio_buf, list_filter->buf_size,
										16, audioDev[id].audioInChn[i].vol.vol);
								tmp->buf_size = list_filter->buf_size;
								tmp->timestamp = list_filter->timestamp;
								/* ref */
								if (0 != audioin_dev_chn_ref_enable(id, i)) {
									if (NULL != tmp->ref) {
										if (NULL != tmp->ref->audio_buf) {
											memcpy(tmp->ref->audio_buf, list_play->audio_buf, list_play->buf_size);
										} else {
											IMP_LOG_ERR(TAG, "ERROR: NULL != tmp->ref->audio_buf\n");
										}
									} else {
										IMP_LOG_ERR(TAG, "ERROR: NULL != tmp->ref\n");
									}
								}
								Audio_Chn_Release_List(&audioDev[id].audioInChn[i].frameHeadIn, &audioDev[id].audioInChn[i].frameHeadOut, LIST_NOT_USED);
							}
							Chn_UnLock(&audioDev[id].audioInChn[i].chnLock);
						}
					}
				}
#ifdef AUDIO_DEBUG
				fwrite(list_play->audio_buf, 1, list_play->buf_size, file_play);
				fwrite(list_record->audio_buf, 1, list_record->buf_size, file_record);
				fwrite(list_filter->audio_buf, 1, list_filter->buf_size, file_filter);
#endif
			}

			Audio_Release_List(MODE_RECORD, LIST_USED);
			Audio_Release_List(MODE_FILTER, LIST_NOT_USED);
			Audio_Release_List(MODE_PLAY, LIST_USED);
		} else {

			AudioList *list_record = Audio_Prefetch_List(MODE_RECORD, LIST_USED);
			if(list_record == NULL) {
				IMP_LOG_ERR(TAG, "ERROR: list record is NULL\n");
				continue;
			}
#if 1
			pthread_mutex_lock(&ns_mutex);
			if (enable_noise_suppression) {
				ret = IngenicNs_Process(&handle_ns, (short *)list_record->audio_buf, (short *)list_record->audio_buf, list_record->buf_size);
				if (ret != 0) {
					IMP_LOG_ERR(TAG, "IngenicNs_Process error.\n");
					pthread_mutex_unlock(&ns_mutex);
					return NULL;
				}
			}
			pthread_mutex_unlock(&ns_mutex);
#endif
			if (enable_ai_agc_suppression) {
				ret = IngenicAgc_Process(handle_ai_agc, (short *)list_record->audio_buf, agc_sample_i, (short *)list_record->audio_buf,
											127, &outMicLevel, 0, &saturationWarning, list_record->buf_size);
				if (ret != 0) {
					IMP_LOG_ERR(TAG, "IngenicAgc_Process ai error.\n");
					return NULL;
				}
			}
#if 0
			if (enable_ai_hpf_suppression) {
				ret = IngenicHPF_Filter(&handle_ai_hpf, (short *)list_record->audio_buf, list_record->buf_size);
				if (ret != 0) {
					IMP_LOG_ERR(TAG, "IngenicHPF_Filter ai error.\n");
					return NULL;
				}
			}
#endif
			if(audioDev[id].aiEnable) {
				int i = 0;
				for(i = 0; i < MAX_AUDIO_IN_CHN; i++) {
					if(audioDev[id].audioInChn[i].enable == 1) {
						Chn_Lock(&audioDev[id].audioInChn[i].chnLock);
						AudioList *tmp = Audio_Chn_Prefetch_List(&audioDev[id].audioInChn[i].frameHeadIn, &audioDev[id].audioInChn[i].frameHeadOut, LIST_NOT_USED);
						if(tmp) {
							Audio_Set_Volume((char *)list_record->audio_buf, (char *)tmp->audio_buf, list_record->buf_size,
									16, audioDev[id].audioInChn[i].vol.vol);
							tmp->buf_size = list_record->buf_size;
							tmp->timestamp = list_record->timestamp;
							Audio_Chn_Release_List(&audioDev[id].audioInChn[i].frameHeadIn, &audioDev[id].audioInChn[i].frameHeadOut, LIST_NOT_USED);
						}
						Chn_UnLock(&audioDev[id].audioInChn[i].chnLock);
					}
				}
			}
			Audio_Release_List(MODE_RECORD, LIST_USED);
		}

		pthread_mutex_unlock(&aec_mutex);
		if(Audio_Record_is_ready()) {
			Audio_Record_Post();
		}
	}
#ifdef AUDIO_DEBUG
	fclose(file_record);
	fclose(file_filter);
	fclose(file_play);
#endif
	pthread_exit(0);
}

#define SNDCTL_EXT_SET_RECORD_VOLUME        _SIOR ('P', 103, int)
static int Audio_Record_Open(SUAudioMicIndex id, IMPAudioSampleRate samplerate)
{
	int ret = -1;
	if (id == AUDIO_MIC_D) {/*DIGITAL MIC*/
		dmic_fd = open(DMIC_NAME, O_RDONLY | O_CLOEXEC, 0666);
		if (dmic_fd < 0) {
			IMP_LOG_ERR(TAG, "open jz-dmic error\n");
			return -1;
		}
		if (samplerate) {
			if ((samplerate != AUDIO_SAMPLE_RATE_8000) && (samplerate != AUDIO_SAMPLE_RATE_16000)) {
				IMP_LOG_ERR(TAG, "set digital mic samplerate error! the samplerate just support 8K & 16K\n");
				return -1;
			}
			if ((ret = ioctl(dmic_fd, DMIC_SET_SAMPLERATE, &samplerate)) < 0) {
				IMP_LOG_ERR(TAG, "ioctl dmic error\n");
				return -1;
			}
		}
	} else if (id == AUDIO_MIC_A) {/*ANALOG MIC*/
		if (1 == audio_client) {
			amic_fd_client = audioin_open();
			if (amic_fd_client == 0) {
				IMP_LOG_ERR(TAG, "open dev.dsp error\n");
				return -1;
			}
		} else {
			amic_fd = open(AMIC_NAME, O_RDONLY | O_CLOEXEC, 0666);
			if (amic_fd < 0) {
				IMP_LOG_ERR(TAG, "open dev.dsp error\n");
				return -1;
			}
		}
		if (samplerate) {
			if (1 == audio_client) {
				if ((ret = audioin_ioctl(amic_fd_client, AUDIOIN_IOCTL_SNDCTL_DSP_SPEED, &samplerate)) != 0) {
					IMP_LOG_ERR(TAG, "ioctl amic set samplerate error\n");
					return -1;
				}
			} else {
				if ((ret = ioctl(amic_fd, SNDCTL_DSP_SPEED, &samplerate)) != 0) {
					IMP_LOG_ERR(TAG, "ioctl amic set samplerate error\n");
					return -1;
				}
			}
		}
	} else {
		IMP_LOG_ERR(TAG, "Audio MIC index is not support this id(%d)\n", id);
		return -1;
	}

	return 0;
}

int IMP_Audio_RecordInit(int audioDevId)
{
	int ret = -1;
	static SUAudioMicIndex index = -1;

	if(audioDevId == 0) {
		index = AUDIO_MIC_D;
	} else if(audioDevId == 1) {
		index = AUDIO_MIC_A;
	} else {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(audioDev[audioDevId].audioIAttr.soundmode != AUDIO_SOUND_MODE_MONO ) {
		IMP_LOG_ERR(TAG, "Audio In sound mode error, only support AUDIO_SOUND_MODE_MONO\n");
		return -1;
	}

	if(audioDev[audioDevId].audioIAttr.bitwidth != AUDIO_BIT_WIDTH_16) {
		IMP_LOG_ERR(TAG, "Audio In bit width error, only support AUDIO_BIT_WIDTH_16\n");
		return -1;
	}

	ret = Audio_Record_Open(index, audioDev[audioDevId].audioIAttr.samplerate);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Record Open error\n");
		return ret;
	}

	ret = Audio_List_Init();
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio List Init error\n");
		return ret;
	}

	record_sample_rate = audioDev[audioDevId].audioIAttr.samplerate;
	audio_record_thread_run = 1;
	ret = pthread_create(&audio_record_thread_t, NULL, Audio_Record_Thread, (void *)&index);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "audio record thread cretate error\n");
		return ret;
	}

	return 0;
}

int IMP_Audio_Record(SUAudioRecordAttr *attr)
{
	int size = 0;
	AudioList *list = NULL;
	if((attr == NULL) || (attr->buf == NULL)) {
		IMP_LOG_ERR(TAG, "IMP_Audio_Record: attr is NULL or attr->buf is NULL.\n");
		return -1;
	}

	while(1) {
		if(IMP_Audio_GetEchoSuppressorState()) {
			list = Audio_Prefetch_List(MODE_FILTER, LIST_USED);
		} else {
			list = Audio_Prefetch_List(MODE_RECORD, LIST_USED);
		}

		if(list == NULL) {
			Audio_Record_Wait(5000);
		} else {
			break;
		}
	}

	if(attr->max_size < list->buf_size) {
		IMP_LOG_ERR(TAG, "Error: Audio Record buf size is too small.\n");
		IMP_LOG_ERR(TAG, "The size of the Audio Record buf must have at least %d\n", MAX_AUDIO_BUF_SIZE);
		return -1;
	}

	memcpy(attr->buf, list->audio_buf, list->buf_size);
	attr->timestamp.tv_sec = list->timestamp / 1000000;
	attr->timestamp.tv_usec = list->timestamp % 1000000;
	size = list->buf_size;

	if(IMP_Audio_GetEchoSuppressorState()) {
		Audio_Release_List(MODE_FILTER, LIST_USED);
	} else {
		Audio_Release_List(MODE_RECORD, LIST_USED);
	}
	return size;
}

int IMP_Audio_RecordDeinit(int id)
{
	audio_record_thread_run = 0;
	Audio_Record_Post();
	pthread_join(audio_record_thread_t, NULL);

	if(id == 0) {
		id = AUDIO_MIC_D;
	} else if(id == 1) {
		id = AUDIO_MIC_A;
	} else {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if ((id == AUDIO_MIC_D) && (dmic_fd > 0)) {
		close(dmic_fd);
		dmic_fd = -1;
	}
	if (1 == audio_client) {
		if ((id == AUDIO_MIC_A) && (amic_fd_client != NULL)) {
			audioin_close(amic_fd_client);
			amic_fd_client = NULL;
		}
	} else {
		if ((id == AUDIO_MIC_A) && (amic_fd > 0)) {
			close(amic_fd);
			amic_fd = -1;
		}
	}

	if(Audio_List_DeInit()) {
		IMP_LOG_ERR(TAG, "Audio List Deinit error\n");
		return -1;
	}

	return 0;
}

/**
 * Audio Play interface
 */
#define DSP_DEVICE  "/dev/dsp"
#define SAMPLERATE_PLAY		AUDIO_SAMPLE_RATE_8000

static void *audio_fd_client = NULL;
static int audio_fd = -1;
static int last_id = -1;
static int spk_ref_num = 0;

enum snd_device_t {
	SND_DEVICE_DEFAULT = 0,

	SND_DEVICE_CURRENT,
	SND_DEVICE_HANDSET,
	SND_DEVICE_HEADSET,	//fix for mid pretest
	SND_DEVICE_SPEAKER,	//fix for mid pretest
	SND_DEVICE_SPEAKER_BACK,

	SND_DEVICE_HEADPHONE,

	SND_DEVICE_BT,
	SND_DEVICE_BT_EC_OFF,
	SND_DEVICE_HEADSET_AND_SPEAKER,
	SND_DEVICE_TTY_FULL,
	SND_DEVICE_CARKIT,

	SND_DEVICE_FM_SPEAKER,
	SND_DEVICE_FM_HEADSET,
	SND_DEVICE_BUILDIN_MIC,
	SND_DEVICE_HEADSET_MIC,
	SND_DEVICE_HDMI = 15,	//fix for mid pretest

	SND_DEVICE_LOOP_TEST = 16,

	SND_DEVICE_CALL_START,							//call route start mark
	SND_DEVICE_CALL_HEADPHONE = SND_DEVICE_CALL_START,
	SND_DEVICE_CALL_HEADSET,
	SND_DEVICE_CALL_HANDSET,
	SND_DEVICE_CALL_SPEAKER,
	SND_DEVICE_HEADSET_RECORD_INCALL,
	SND_DEVICE_BUILDIN_RECORD_INCALL,
	SND_DEVICE_CALL_END = SND_DEVICE_BUILDIN_RECORD_INCALL,	//call route end mark

	SND_DEVICE_COUNT
};

#define SNDCTL_EXT_SET_BUFFSIZE				_SIOR ('P', 100, int)
#define SNDCTL_EXT_SET_DEVICE               _SIOR ('P', 99, int)
#define SNDCTL_EXT_SET_STANDBY              _SIOR ('P', 98, int)
#define SNDCTL_EXT_START_BYPASS_TRANS       _SIOW ('P', 97, struct spipe_info)
#define SNDCTL_EXT_STOP_BYPASS_TRANS        _SIOW ('P', 96, struct spipe_info)
#define SNDCTL_EXT_DIRECT_GETINODE          _SIOR ('P', 95, struct direct_info)
#define SNDCTL_EXT_DIRECT_PUTINODE          _SIOW ('P', 94, struct direct_info)
#define SNDCTL_EXT_DIRECT_GETONODE          _SIOR ('P', 93, struct direct_info)
#define SNDCTL_EXT_DIRECT_PUTONODE          _SIOW ('P', 92, struct direct_info)
#define SNDCTL_EXT_STOP_DMA					_SIOW ('P', 91, int)
#define SNDCTL_EXT_SET_REPLAY_VOLUME        _SIOR ('P', 90, int)
#define SND_MUTE_UNMUTED        0
#define SND_MUTE_MUTED          1

static int set_device_samplerate_client(void *fd, IMPAudioSampleRate *samplerate)
{
	int ret = 0;

	if (fd <= 0)
		return -1;
	ret = audioout_ioctl(fd, AUDIOOUT_IOCTL_SNDCTL_DSP_SPEED, samplerate);
	if (ret)
		ret = -1;

	return ret;
}
static int set_device_samplerate(int fd, IMPAudioSampleRate *samplerate)
{
	int ret = 0;

	if (fd <= 0)
		return -1;
	ret = ioctl(fd, SNDCTL_DSP_SPEED, samplerate);
	if (ret)
		ret = -1;

	return ret;
}

int IMP_Audio_PlayInit(IMPAudioSampleRate samplerate)
{
	int ret = -1;

	spk_ref_num++;

	if (1 == audio_client) {
		if(audio_fd_client != NULL) {
			IMP_LOG_WARN(TAG, "dsp is already opened\n");
			return 0;
		}

	}else {
		if(audio_fd > 0) {
			IMP_LOG_WARN(TAG, "dsp is already opened\n");
			return 0;
		}
	}
	if (1 == audio_client) {
		audio_fd_client = audioout_open();
		if (audio_fd_client == 0) {
			IMP_LOG_ERR(TAG, "open dsp error\n");
			spk_ref_num--;
			return -1;
		}
	} else {
		audio_fd = open(DSP_DEVICE, O_WRONLY | O_CLOEXEC);
		if (audio_fd < 0) {
			IMP_LOG_ERR(TAG, "open dsp error\n");
			spk_ref_num--;
			return -1;
		}
	}
	if (samplerate) {
		if (1 == audio_client) {
			ret = set_device_samplerate_client(audio_fd_client, &samplerate);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "audio set play samplerate error!!\n");
				spk_ref_num--;
				return -1;
			}
		} else {
			ret = set_device_samplerate(audio_fd, &samplerate);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "audio set play samplerate error!!\n");
				spk_ref_num--;
				return -1;
			}
		}
	}

	play_sample_rate = samplerate;
	if(Audio_List_Init()) {
		IMP_LOG_ERR(TAG, "Audio List Init error\n");
		spk_ref_num--;
		return -1;
	}

	return 0;
}

static int Audio_Play(SUAudioPlayAttr *attr)
{
	int ret;
	int device_mode;
	int written = 0;
	int count = attr->bytes;
	char *tmp_buf = attr->buf;

	if (1 == audio_client) {
		if (audio_fd_client == 0) {
			IMP_LOG_ERR(TAG, "Audio Play is not init\n");
			return -1;
		}
	} else {
		if (audio_fd < 0) {
			IMP_LOG_ERR(TAG, "Audio Play is not init\n");
			return -1;
		}
	}

	if (attr->buf == NULL) {
		IMP_LOG_ERR(TAG, "The parameter [ buf ] is NULL, error!!\n");
		return -1;
	}
#if 0
	if (last_id != attr->id) {
		last_id = attr->id;
		if (attr->id == AUDIO_SPK_DEFAULT) {
			device_mode = SND_DEVICE_SPEAKER;
		} else if (attr->id == AUDIO_SPK_BACK) {
			device_mode = SND_DEVICE_SPEAKER_BACK;
		} else {
			IMP_LOG_ERR(TAG, "set attr->id error.\n");
			return -1;
		}
		if (1 == audio_client) {
			if ((ret = audioout_ioctl(audio_fd_client, AUDIOOUT_IOCTL_SNDCTL_EXT_SET_DEVICE, &device_mode)) < 0) {
				IMP_LOG_ERR(TAG, "opn dsp error\n");
				return -1;
			}
		} else {
			if ((ret = ioctl(audio_fd, SNDCTL_EXT_SET_DEVICE, &device_mode)) < 0) {
				IMP_LOG_ERR(TAG, "opn dsp error\n");
				return -1;
			}
		}
	}
#endif
	/* if (enable_ao_agc_suppression) { */
	/* 	int outMicLevel = 0; */
	/* 	unsigned char saturationWarning = 0; */
	/* 	ret = IngenicAgc_Process(handle_ao_agc, (short *)tmp_buf, agc_sample_o, (short *)tmp_buf, */
	/* 			127, &outMicLevel, 0, &saturationWarning, count); */
	/* 	if (ret != 0) { */
	/* 		IMP_LOG_ERR(TAG, "IngenicAgc_Process error.\n"); */
	/* 		return -1; */
	/* 	} */
	/* } */

#ifdef AUDIO_OUT_DEBUG
	if (NULL == file_play_2) {
		file_play_2 = fopen("/tmp/play_2_after_agc.pcm", "wb");
	}
	fwrite((char *)tmp_buf, 1, count, file_play_2);
#endif

/*	if (enable_ao_hpf_suppression) {
		ret = IngenicHPF_Filter(&handle_ao_hpf, (short *)tmp_buf, count);
		if (ret != 0) {
			IMP_LOG_ERR(TAG, "IngenicHPF_Filter error.\n");
			return -1;
		}
	}
*/
	while(count > 3) {
		if (1 == audio_client) {
			written = audioout_write(audio_fd_client, tmp_buf , count);
		} else {
			written = write(audio_fd, tmp_buf , count);
		}
		if (written < 0) {
			IMP_LOG_ERR(TAG, "write audio_fd error\n");
			return -1;
		}
		count -= written;
		tmp_buf += written;
	}

	return attr->bytes - count;
}

int IMP_Audio_Play(SUAudioPlayAttr *attr,  int sync)
{
	if((IMP_Audio_GetEchoSuppressorState()||(audioin_ref_enable())) && audio_record_thread_run) {
		AudioList *list = NULL;
		if (attr->buf == NULL) {
			IMP_LOG_ERR(TAG, "IMP_Audio_Play The parameter [ buf ] is NULL, error!!\n");
			return -1;
		}

		if(attr->bytes != MAX_AUDIO_BUF_SIZE) {
			IMP_LOG_ERR(TAG, "IMP_Audio_Play Error:Playback buf size is (%d)\n", attr->bytes);
			IMP_LOG_ERR(TAG, "Echo Suppressor Function is Enabled,\n");
			IMP_LOG_ERR(TAG, "So the Playback buf size must be equal to %d\n", MAX_AUDIO_BUF_SIZE);
			return -1;
		}

		while(1) {
			list = Audio_Prefetch_List(MODE_PLAY, LIST_NOT_USED);
			if(list == NULL) {
				usleep(SLEEP_TIME);
				continue;
			} else {
				break;
			}
		}

		memcpy(list->audio_buf, attr->buf, attr->bytes);
		list->buf_size = attr->bytes;

		if(sync != 0) {
			echo_suppressor_clear = 0;
			Audio_Play_Wait();
		}

		list->timestamp = Audio_Get_Time_Stamp();
		Audio_Release_List(MODE_PLAY, LIST_NOT_USED);
	}

	return Audio_Play(attr);;
}

int IMP_Audio_PlayDeinit(void)
{
	Audio_Play_Post();
	spk_ref_num--;

	if(spk_ref_num > 0) {
		return 0;
	}
	if (1 == audio_client) {
		if (NULL != audio_fd_client) {
			audioout_close(audio_fd_client);
			audio_fd_client = NULL;
			last_id = -1;
		}
	} else {
		if(audio_fd > 0) {
			close(audio_fd);
			audio_fd = -1;
			last_id = -1;
		}
	}

	if(Audio_List_DeInit()) {
		IMP_LOG_ERR(TAG, "Audio List Deinit error\n");
		return -1;
	}

	return 0;
}


/* Audio IN API */
int IMP_AI_SetPubAttr(int audioDevId, IMPAudioIOAttr *attr)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(attr->chnCnt > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "set pub attr error, invalid chnCnt.\n");
		return -1;
	}

	SAMPLE_RATE = attr->samplerate;
	SAMPLE_TIME = 10;

	if((((1000 * attr->numPerFrm) / SAMPLE_RATE) % SAMPLE_TIME) != 0) {
		IMP_LOG_ERR(TAG, "The number of samples(numPerFrm) must be an integer number of 10ms (10ms * n).\n");
		return -1;
	}

	AUDIO_ECHO_SUPPRESSOR_SIZE = (SAMPLE_RATE * sizeof(short) * SAMPLE_TIME / 1000);
	MAX_AUDIO_BUF_SIZE = AUDIO_ECHO_SUPPRESSOR_SIZE * (((1000 * attr->numPerFrm) / SAMPLE_RATE) / SAMPLE_TIME);
	MAX_AUDIO_BUF_NUM = attr->frmNum;

	memcpy(&audioDev[audioDevId].audioIAttr, attr, sizeof(IMPAudioIOAttr));
	return 0;
}

int IMP_AI_GetPubAttr(int audioDevId, IMPAudioIOAttr *attr)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	memcpy(attr, &audioDev[audioDevId].audioIAttr, sizeof(IMPAudioIOAttr));
	return 0;
}

int IMP_AI_Enable(int audioDevId)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(audioDev[audioDevId].aiEnable == 1) {
		IMP_LOG_ERR(TAG, "Audio Device is already enabled.\n");
		return 0;
	}

	if(IMP_Audio_RecordInit(audioDevId) != 0) {
		IMP_LOG_ERR(TAG, "Audio Device enable error.\n");
		return -1;
	}
	audioDev[audioDevId].aiEnable = 1;
	return 0;
}

int IMP_AI_Disable(int audioDevId)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(audioDev[audioDevId].aiEnable == 0) {
		IMP_LOG_ERR(TAG, "Audio Device is already Disabled.\n");
		return 0;
	}

	audioDev[audioDevId].aiEnable = 0;
	if(IMP_Audio_RecordDeinit(audioDevId) != 0) {
		IMP_LOG_ERR(TAG, "Audio Device Disable error\n");
		return -1;
	}
	return 0;
}

static int keep_ai_old_vol = 0;
static double keep_ai_old_vol_coef = 0.0;
int IMP_AI_EnableChn(int audioDevId, int aiChn)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return 0;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(audioDev[audioDevId].audioInChn[aiChn].enable == 1) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio channel is already enabled.\n", __func__, __LINE__);
		return 0;
	}

	if(audioDev[audioDevId].audioInChn[aiChn].audioIChnParam.usrFrmDepth < 2 ||
			audioDev[audioDevId].audioInChn[aiChn].audioIChnParam.usrFrmDepth > MAX_AUDIO_BUF_NUM) {
		IMP_LOG_ERR(TAG, "The channel parameters are not set.\n");
		return -1;
	}

	pthread_mutex_init(&audioDev[audioDevId].audioInChn[aiChn].chnLock, NULL);

	Chn_Lock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
	if(audioDev[audioDevId].audioInChn[aiChn].frameHeadIn == NULL) {
		audioDev[audioDevId].audioInChn[aiChn].frameHeadIn = Audio_List_Alloc(MODE_AI_CHN,
				audioDev[audioDevId].audioInChn[aiChn].audioIChnParam.usrFrmDepth);
		if(audioDev[audioDevId].audioInChn[aiChn].frameHeadIn == NULL) {
			IMP_LOG_ERR(TAG, "record head list alloc error\n");
			Chn_UnLock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
			return -1;
		}
		audioDev[audioDevId].audioInChn[aiChn].frameHeadOut =
			audioDev[audioDevId].audioInChn[aiChn].frameHeadIn;
	}
	Chn_UnLock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);

	/*Set default ai vol.*/
	keep_ai_old_vol_coef = pow(10, ((60.0 * 0.5) - 30.0) / 20.0);
	audioDev[audioDevId].audioInChn[aiChn].vol.vol = keep_ai_old_vol_coef;
	keep_ai_old_vol = 60;

	audioDev[audioDevId].audioInChn[aiChn].enable = 1;
	return 0;
}

int IMP_AI_DisableChn(int audioDevId, int aiChn)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(audioDev[audioDevId].audioInChn[aiChn].enable == 0) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio channel is already disabled.\n", __func__, __LINE__);
		return 0;
	}

	audioDev[audioDevId].audioInChn[aiChn].enable = 0;

	Chn_Lock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
	if(audioDev[audioDevId].audioInChn[aiChn].frameHeadIn != NULL) {
		if(Audio_List_Free(audioDev[audioDevId].audioInChn[aiChn].frameHeadIn)) {
			IMP_LOG_ERR(TAG, "Audio list Deinit (free play list) failed\n");
			Chn_UnLock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
			return -1;
		}
		audioDev[audioDevId].audioInChn[aiChn].frameHeadIn = NULL;
		audioDev[audioDevId].audioInChn[aiChn].frameHeadOut = NULL;
	}
	Chn_UnLock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);

	pthread_mutex_destroy(&audioDev[audioDevId].audioInChn[aiChn].chnLock);

	IMP_Audio_DisableEchoSuppressor();

	return 0;
}

int IMP_AI_GetFrame(int audioDevId, int aiChn, IMPAudioFrame *frm, IMPBlock block)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(!audioDev[audioDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d Audio Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	do {
		Chn_Lock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
		AudioList *list = Audio_Chn_Prefetch_List(&audioDev[audioDevId].audioInChn[aiChn].frameHeadIn,
				&audioDev[audioDevId].audioInChn[aiChn].frameHeadOut, LIST_USED);
		if(list) {
			audioDev[audioDevId].audioInChn[aiChn].audioFrame.virAddr = (uint32_t *)list->audio_buf;
			audioDev[audioDevId].audioInChn[aiChn].audioFrame.len = list->buf_size;
			audioDev[audioDevId].audioInChn[aiChn].audioFrame.timeStamp = list->timestamp;

			audioDev[audioDevId].audioInChn[aiChn].audioFrame.bitwidth = audioDev[audioDevId].audioIAttr.bitwidth;
			audioDev[audioDevId].audioInChn[aiChn].audioFrame.soundmode = audioDev[audioDevId].audioIAttr.soundmode;
			memcpy(frm, &audioDev[audioDevId].audioInChn[aiChn].audioFrame, sizeof(IMPAudioFrame));
			Chn_UnLock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
			return 0;
		}
		Chn_UnLock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);

		if(block == BLOCK)
			Audio_Record_Wait(5000);
	} while(block == BLOCK);

	return -1;
}
//#define DEBUG_REF
#ifdef DEBUG_REF
FILE * file_get_frame = NULL;
FILE * file_get_frame_ref = NULL;
int file_rec_num = 15000;
#endif


int IMP_AI_GetFrameAndRef(int audioDevId, int aiChn, IMPAudioFrame *frm, IMPAudioFrame *ref, IMPBlock block)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(!audioDev[audioDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d Audio Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	do {
		Chn_Lock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
		AudioList *list = Audio_Chn_Prefetch_List(&audioDev[audioDevId].audioInChn[aiChn].frameHeadIn,
				&audioDev[audioDevId].audioInChn[aiChn].frameHeadOut, LIST_USED);
		if(list) {
			audioDev[audioDevId].audioInChn[aiChn].audioFrame.virAddr = (uint32_t *)list->audio_buf;
			audioDev[audioDevId].audioInChn[aiChn].audioFrame.len = list->buf_size;
			audioDev[audioDevId].audioInChn[aiChn].audioFrame.timeStamp = list->timestamp;

			audioDev[audioDevId].audioInChn[aiChn].audioFrame.bitwidth = audioDev[audioDevId].audioIAttr.bitwidth;
			audioDev[audioDevId].audioInChn[aiChn].audioFrame.soundmode = audioDev[audioDevId].audioIAttr.soundmode;
			memcpy(frm, &audioDev[audioDevId].audioInChn[aiChn].audioFrame, sizeof(IMPAudioFrame));
			/* ref */
			if (NULL != list->ref) {
				if (NULL != list->ref->audio_buf) {
					memcpy(ref, &audioDev[audioDevId].audioInChn[aiChn].audioFrame, sizeof(IMPAudioFrame));
					ref->virAddr = (uint32_t *)list->ref->audio_buf;
				}
			}

#ifdef DEBUG_REF
			if ((NULL == file_get_frame)&&(file_rec_num > 0)) {
				file_get_frame = fopen("/tmp/get_frame.pcm", "wb");
			}
			if ((NULL == file_get_frame_ref)&&(file_rec_num > 0)){
				file_get_frame_ref = fopen("/tmp/get_frame_ref.pcm", "wb");
			}
			if (NULL != file_get_frame)
				fwrite(frm->virAddr, 1, frm->len, file_get_frame);
			if (NULL != file_get_frame_ref)
				fwrite(ref->virAddr, 1, ref->len, file_get_frame_ref);
			file_rec_num--;
			if ((file_rec_num <= 0)&&(NULL != file_get_frame)) {
				fclose(file_get_frame);
				file_get_frame = NULL;
			}
			if ((file_rec_num <= 0)&&(NULL != file_get_frame_ref)) {
				fclose(file_get_frame_ref);
				file_get_frame_ref = NULL;
			}
#endif
			Chn_UnLock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
			return 0;
		}
		Chn_UnLock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);

		if(block == BLOCK)
			Audio_Record_Wait(5000);
	} while(block == BLOCK);

	return -1;

}

int IMP_AI_EnableAecRefFrame(int audioDevId, int aiChn, int audioAoDevId, int aoChn)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(!audioDev[audioDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d Audio Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	{
		Chn_Lock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
		AudioList *list = audioDev[audioDevId].audioInChn[aiChn].frameHeadIn;
		AudioList *tmplist = list;
		uint8_t listnum = audioDev[audioDevId].audioInChn[aiChn].audioIChnParam.usrFrmDepth;
		int i = 0;
		for (i = 0; i < listnum; i++) {
			if (NULL != tmplist->ref) {
				IMP_LOG_ERR(TAG, "err: tmplist->ref = %p\n", tmplist->ref);
			}
			tmplist->ref = (AudioList *)malloc(sizeof(AudioList));
			if(NULL == tmplist->ref) {
				IMP_LOG_ERR(TAG, "alloc AudioList head failed\n");
				return -1;
			} else {
				memset(tmplist->ref, 0x0, sizeof(AudioList));
				tmplist->ref->audio_buf = malloc(MAX_AUDIO_BUF_SIZE);
				if(NULL == tmplist->ref->audio_buf) {
					IMP_LOG_ERR(TAG, "alloc head audio buf failed\n");
					return -1;
				}
			}
			tmplist = tmplist->next;
		}

		if (list != tmplist) {
			IMP_LOG_WARN(TAG, "list = %p, tmplist = %p\n", list, tmplist);
		}
		audioDev[audioDevId].audioInChn[aiChn].enable_ref = 1;
		Chn_UnLock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
	}
	Audio_Lock();
	echo_time_sync = 1;
	Audio_Unlock();
	return 0;
}

int IMP_AI_DisableAecRefFrame(int audioDevId, int aiChn, int audioAoDevId, int aoChn)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(!audioDev[audioDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d Audio Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	Audio_Clear_List(MODE_RECORD);
	Audio_Clear_List(MODE_PLAY);
	Audio_Clear_List(MODE_FILTER);

	echo_suppressor_start = 0;
	echo_suppressor_clear = 0;
	echo_time_sync = 0;

	Audio_Play_Post();
	Audio_Record_Post();

	{
		Chn_Lock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
		AudioList *list = audioDev[audioDevId].audioInChn[aiChn].frameHeadIn;
		AudioList *tmplist = list;
		uint8_t listnum = audioDev[audioDevId].audioInChn[aiChn].audioIChnParam.usrFrmDepth;
		int i = 0;
		for (i = 0; i < listnum; i++) {
			if (NULL != tmplist->ref) {
				if(NULL != tmplist->ref->audio_buf) {
					free(tmplist->ref->audio_buf);
				} else {
					IMP_LOG_WARN(TAG, "audio_buf == NULL\n");
				}
				free(tmplist->ref);
			} else {

				IMP_LOG_WARN(TAG, "ref == NULL\n");
			}
			tmplist = tmplist->next;
		}
		if (list != tmplist) {
			IMP_LOG_WARN(TAG, "list = %p, tmplist = %p\n", list, tmplist);
		}
		audioDev[audioDevId].audioInChn[aiChn].enable_ref = 0;
		Chn_UnLock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
	}
	return 0;
}

int IMP_AI_PollingFrame(int audioDevId, int aiChn, unsigned int timeout_ms)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(!audioDev[audioDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d Audio Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	Chn_Lock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
	AudioList *list = Audio_Chn_Prefetch_List(&audioDev[audioDevId].audioInChn[aiChn].frameHeadIn,
			&audioDev[audioDevId].audioInChn[aiChn].frameHeadOut, LIST_USED);
	Chn_UnLock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);

	if (list == NULL && Audio_Record_Wait(timeout_ms) != 0) {
		IMP_LOG_ERR(TAG, "%s %d Audio polling timeout.\n", __func__, __LINE__);
		return -1;
	}

	return 0;
}

int IMP_AI_ReleaseFrame(int audioDevId, int aiChn, IMPAudioFrame *frm)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(!audioDev[audioDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d Audio Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	Chn_Lock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);
	Audio_Chn_Release_List(&audioDev[audioDevId].audioInChn[aiChn].frameHeadIn,
			&audioDev[audioDevId].audioInChn[aiChn].frameHeadOut, LIST_USED);
	audioDev[audioDevId].audioInChn[aiChn].audioFrame.virAddr = NULL;
	audioDev[audioDevId].audioInChn[aiChn].audioFrame.len = 0;
	audioDev[audioDevId].audioInChn[aiChn].audioFrame.timeStamp = 0;
	audioDev[audioDevId].audioInChn[aiChn].audioFrame.seq++;
	Chn_UnLock(&audioDev[audioDevId].audioInChn[aiChn].chnLock);

	return 0;
}

int IMP_AI_SetChnParam(int audioDevId, int aiChn, IMPAudioIChnParam *chnParam)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(chnParam->usrFrmDepth < 2 || chnParam->usrFrmDepth > MAX_AUDIO_FRAME_NUM) {
		IMP_LOG_ERR(TAG, "Invalid audio channel usrFrmDepth.\n");
		return -1;
	}

	audioDev[audioDevId].audioInChn[aiChn].audioIChnParam.usrFrmDepth = chnParam->usrFrmDepth;

	return 0;
}

int IMP_AI_GetChnParam(int audioDevId, int aiChn, IMPAudioIChnParam *chnParam)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	chnParam->usrFrmDepth = audioDev[audioDevId].audioInChn[aiChn].audioIChnParam.usrFrmDepth;

	return 0;
}

int IMP_AI_EnableAec(int AiDevId, int aiChn, int AoDevId, int aoChn)
{
	if(AiDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(!audioDev[AiDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(!audioDev[AiDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d Audio Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	IMP_LOG_INFO(TAG, "AI AEC ENABLE\n");

	IMP_Audio_EnableEchoSuppressor(AiDevId);
	return 0;
}

int IMP_AI_DisableAec(int AiDevId, int aiChn)
{
	if(AiDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(!audioDev[AiDevId].aiEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(!audioDev[AiDevId].audioInChn[aiChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d Audio Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	IMP_LOG_INFO(TAG, "AI AEC DISABLE\n");

	IMP_Audio_DisableEchoSuppressor();
	return 0;
}

int IMP_AI_SetVolMute(int audioDevId, int aiChn, int mute)
{
	if (mute == 1) {
		audioDev[audioDevId].audioInChn[aiChn].vol.vol = 0.0;
		audioDev[audioDevId].audioInChn[aiChn].vol.mute = mute;
	} else if (mute == 0) {
		audioDev[audioDevId].audioInChn[aiChn].vol.vol = keep_ai_old_vol_coef;
		audioDev[audioDevId].audioInChn[aiChn].vol.mute = mute;
	} else {
		IMP_LOG_ERR(TAG, "Invalid AI Mute Value.\n");
		return -1;
	}

	return 0;
}

int IMP_AI_SetVol(int audioDevId, int aiChn, int aiVol)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	IMPAudioVol *vol = (IMPAudioVol *)malloc(sizeof(IMPAudioVol));
	if(vol == NULL) {
		IMP_LOG_ERR(TAG, "vol is NULL.\n");
		return -1;
	}
	vol->minVol = -30;
	vol->maxVol = 120;
	vol->mute   = 0;

	if (aiVol < vol->minVol) aiVol = -30;
	if (aiVol > vol->maxVol) aiVol = 120;

	if (aiVol == -30) {
		vol->vol = 0.0;
	} else {
		/*dB = 20log(Vx / Vo), step is 0.5dB*/
		vol->vol = pow(10, ((((double)aiVol) * 0.5) - 30.0) / 20.0);
	}

	audioDev[audioDevId].audioInChn[aiChn].vol.vol = vol->vol;
	audioDev[audioDevId].audioInChn[aiChn].vol.minVol = vol->minVol;
	audioDev[audioDevId].audioInChn[aiChn].vol.maxVol = vol->maxVol;
	audioDev[audioDevId].audioInChn[aiChn].vol.mute = vol->mute;
	keep_ai_old_vol_coef = vol->vol;
	keep_ai_old_vol = aiVol;

	free(vol);

	return 0;
}

int IMP_AI_GetVol(int audioDevId, int aiChn, int *vol)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(vol == NULL) {
		IMP_LOG_ERR(TAG, "vol is NULL.\n");
		return -1;
	}

	*vol = keep_ai_old_vol;

	return 0;
}

static int keep_old_ai_gain = 0;
int IMP_AI_SetGain(int audioDevId, int aiChn, int aiGain)
{
	int ret = -1;
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	/*TODO: Just suitable T10 Internal Codec,If change Codec,We should modify.*/
	/******************************************\
	| vol :0	1		2	..	30  31	       |
	| gain:-18  -16.5  -15	..	27  28.5 (+DB) |
	\******************************************/

	if (aiGain < 0) aiGain = 0;
	if (aiGain > 31) aiGain = 31;
	if (1 == audio_client) {
		if ((ret = audioin_ioctl(amic_fd_client, AUDIOIN_IOCTL_SNDCTL_EXT_SET_RECORD_VOLUME, &aiGain)) != 0) {
			IMP_LOG_ERR(TAG, "ioctl amic set record volume error\n");
		}
	} else {
		if ((ret = ioctl(amic_fd, SNDCTL_EXT_SET_RECORD_VOLUME, &aiGain)) != 0) {
			IMP_LOG_ERR(TAG, "ioctl amic set record volume error\n");
		}
	}

	keep_old_ai_gain = aiGain;

	return 0;
}

int IMP_AI_GetGain(int audioDevId, int aiChn, int *aiGain)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aiChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	*aiGain = keep_old_ai_gain;

	return 0;
}

/* Audio Out API */
static pthread_mutex_t out_thread_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t out_thread_cond = PTHREAD_COND_INITIALIZER;

static pthread_mutex_t out_send_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t out_send_cond = PTHREAD_COND_INITIALIZER;

static void Audio_Out_Send_Wait()
{
	struct timeval now;
	struct timespec timeout;

	pthread_mutex_lock(&out_send_mutex);
	gettimeofday(&now, NULL);
	timeout.tv_sec = now.tv_sec + 5;
	timeout.tv_nsec = now.tv_usec * 1000;
	if(pthread_cond_timedwait(&out_send_cond, &out_send_mutex, &timeout) == ETIMEDOUT)
		IMP_LOG_WARN(TAG, "Audio Out Send Wait timeout\n");
	pthread_mutex_unlock(&out_send_mutex);
}

static void Audio_Out_Send_Post()
{
	pthread_mutex_lock(&out_send_mutex);
	pthread_cond_signal(&out_send_cond);
	pthread_mutex_unlock(&out_send_mutex);
}

static void Audio_Out_Thread_Wait()
{
	struct timeval now;
	struct timespec timeout;

	pthread_mutex_lock(&out_thread_mutex);
	gettimeofday(&now, NULL);
	timeout.tv_sec = now.tv_sec + 5;
	timeout.tv_nsec = now.tv_usec * 1000;
	if(pthread_cond_timedwait(&out_thread_cond, &out_thread_mutex, &timeout) == ETIMEDOUT)
		;//IMP_LOG_WARN(TAG, "Audio Out thread Wait timeout\n");
	pthread_mutex_unlock(&out_thread_mutex);
}

static void Audio_Out_Thread_Post()
{
	pthread_mutex_lock(&out_thread_mutex);
	pthread_cond_signal(&out_thread_cond);
	pthread_mutex_unlock(&out_thread_mutex);
}

static void *Audio_Out_Stream_Thread(void *argv)
{
	int devID = *(int *)argv;
	int audio_play_thread_wait = 1;
	SUAudioPlayAttr attr;

	char thread_name[64];
	sprintf(thread_name, "Audio-%s", __func__);
	prctl(PR_SET_NAME, (unsigned long)thread_name);

	if(devID == 0) {
		attr.id = AUDIO_SPK_DEFAULT;
	} else if(devID == 1) {
		attr.id = AUDIO_SPK_BACK;
	} else {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		pthread_exit(0);
	}

	int count = 0;
	int anti_shake = 0;
	int empty_buff_size = audioDev[devID].audioOAttr.numPerFrm * sizeof(short);
	char *empty_buff = (char *)malloc(empty_buff_size);
	memset(empty_buff, 0, empty_buff_size);
	echo_time_sync = 0;

	while(audioDev[devID].aoEnable) {
		int i = 0;
		/* gettimeofday(&play_thread_sync, NULL); */
		for(i = 0; i < MAX_AUDIO_OUT_CHN; i++) {
			if(audioDev[devID].audioOutChn[i].enable == 1 && !audioDev[devID].audioOutChn[i].pause) {
				if(echo_time_sync == 1) {
					if(audioDev[devID].audioOutChn[i].status.chnBusyNum < audioDev[devID].audioOutChn[i].status.chnFreeNum) {
						continue;
					}
				}

				AudioList *tmp = Audio_Chn_Prefetch_List(&audioDev[devID].audioOutChn[i].frameHeadIn,
						&audioDev[devID].audioOutChn[i].frameHeadOut, LIST_USED);
				if(tmp) {
					attr.buf = (char *)tmp->audio_buf;
					attr.bytes = tmp->buf_size;
					IMP_Audio_Play(&attr, echo_time_sync);
					if(echo_time_sync == 1){
						echo_time_sync = 0;
						anti_shake = 1;
					}
					count = 0;
					Chn_Lock(&audioDev[devID].audioOutChn[i].chnLock);
					Audio_Chn_Release_List(&audioDev[devID].audioOutChn[i].frameHeadIn,
							&audioDev[devID].audioOutChn[i].frameHeadOut, LIST_USED);

					audioDev[devID].audioOutChn[i].status.chnFreeNum++;
					audioDev[devID].audioOutChn[i].status.chnBusyNum--;
					Chn_UnLock(&audioDev[devID].audioOutChn[i].chnLock);
					Audio_Out_Send_Post();
					audio_play_thread_wait = 0;
				} else if((IMP_Audio_GetEchoSuppressorState()||(audioin_ref_enable())) && anti_shake) {
					attr.buf = (char *)empty_buff;
					attr.bytes = empty_buff_size;
					IMP_Audio_Play(&attr, 0);
					audio_play_thread_wait = 0;

					count ++;
					if(count > 50) {
						count = 0;
						anti_shake = 0;
						echo_time_sync = 1;
						continue;
					}
					usleep(5);
				}
			}
		}

		if(audio_play_thread_wait) {
			Audio_Out_Thread_Wait();
		}

		audio_play_thread_wait = 1;
	}

	free(empty_buff);
	pthread_exit(0);
}

int IMP_AO_SetPubAttr(int audioDevId, IMPAudioIOAttr *attr)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	// TODO: param check and set
	memcpy(&audioDev[audioDevId].audioOAttr, attr, sizeof(IMPAudioIOAttr));
	return 0;
}

int IMP_AO_GetPubAttr(int audioDevId, IMPAudioIOAttr *attr)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	memcpy(attr, &audioDev[audioDevId].audioOAttr, sizeof(IMPAudioIOAttr));
	return 0;
}

int IMP_AO_Enable(int audioDevId)
{
	IMPAudioSampleRate samplerate;
	int ret = -1;
	static int devID;
	devID = audioDevId;

	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(audioDev[audioDevId].aoEnable == 1) {
		IMP_LOG_ERR(TAG, "Audio Device is already enabled.\n");
		return 0;
	}

	samplerate = audioDev[audioDevId].audioOAttr.samplerate;

	if(IMP_Audio_PlayInit(samplerate) != 0) {
		IMP_LOG_ERR(TAG, "Audio Device enable error.\n");
		return -1;
	}
	if (1 == audio_client) {
		ret = audioout_ioctl(audio_fd_client,AUDIOOUT_IOCTL_SNDCTL_DSP_CHANNELS,&audioDev[audioDevId].audioOAttr.soundmode);
		if (ret) {
			IMP_LOG_ERR(TAG, "Audio Device set channel count error.\n");
			ret = -1;
		}
	} else {
		ret = ioctl(audio_fd,SNDCTL_DSP_CHANNELS,&audioDev[audioDevId].audioOAttr.soundmode);
		if (ret) {
			IMP_LOG_ERR(TAG, "Audio Device set channel count error.\n");
			ret = -1;
		}
	}

	audioDev[audioDevId].aoEnable = 1;

	if(pthread_create(&audioDev[audioDevId].aoProcess, NULL, Audio_Out_Stream_Thread, &devID) != 0) {
		IMP_LOG_ERR(TAG, "start audio device thread error\n");
		return -1;
	}

	return 0;

}

int IMP_AO_Disable(int audioDevId)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(audioDev[audioDevId].aoEnable == 0) {
		IMP_LOG_ERR(TAG, "Audio Device is already Disabled.\n");
		return 0;
	}

	audioDev[audioDevId].aoEnable = 0;
	Audio_Out_Thread_Post();
	pthread_join(audioDev[audioDevId].aoProcess, NULL);

	if(IMP_Audio_PlayDeinit() != 0) {
		IMP_LOG_ERR(TAG, "Audio Device Disable error\n");
		return -1;
	}
	return 0;

}

static int keep_ao_old_vol = 0;
static double keep_ao_old_vol_coef = 0.0;
int IMP_AO_EnableChn(int audioDevId, int aoChn)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return 0;
	}

	if(aoChn > MAX_AUDIO_IN_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	Chn_Lock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
	if(audioDev[audioDevId].audioOutChn[aoChn].frameHeadIn == NULL) {
		audioDev[audioDevId].audioOutChn[aoChn].frameHeadIn = Audio_List_Alloc(MODE_AO_CHN,
				audioDev[audioDevId].audioOAttr.frmNum);
		if(audioDev[audioDevId].audioOutChn[aoChn].frameHeadIn == NULL) {
			IMP_LOG_ERR(TAG, "record head list alloc error\n");
			Chn_UnLock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
			return -1;
		}
		audioDev[audioDevId].audioOutChn[aoChn].frameHeadOut =
			audioDev[audioDevId].audioOutChn[aoChn].frameHeadIn;
	}
	audioDev[audioDevId].audioOutChn[aoChn].status.chnTotalNum = audioDev[audioDevId].audioOAttr.frmNum;
	audioDev[audioDevId].audioOutChn[aoChn].status.chnFreeNum = audioDev[audioDevId].audioOAttr.frmNum;
	audioDev[audioDevId].audioOutChn[aoChn].status.chnBusyNum = 0;
	Chn_UnLock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);

	/*Set default ao vol.*/
	keep_ao_old_vol_coef = pow(10, ((60.0 * 0.5) - 30.0) / 20.0);
	audioDev[audioDevId].audioOutChn[aoChn].vol.vol = keep_ao_old_vol_coef;
	keep_ao_old_vol = 60;

	audioDev[audioDevId].audioOutChn[aoChn].enable = 1;
	return 0;

}

int IMP_AO_DisableChn(int audioDevId, int aoChn)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(aoChn > MAX_AUDIO_OUT_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}
	audioDev[audioDevId].audioOutChn[aoChn].enable = 0;

	Chn_Lock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
	if(audioDev[audioDevId].audioOutChn[aoChn].frameHeadIn != NULL) {
		if(Audio_List_Free(audioDev[audioDevId].audioOutChn[aoChn].frameHeadIn)) {
			IMP_LOG_ERR(TAG, "Audio list Deinit (free play list) failed\n");
			Chn_UnLock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
			return -1;
		}
		audioDev[audioDevId].audioOutChn[aoChn].frameHeadIn = NULL;
		audioDev[audioDevId].audioOutChn[aoChn].frameHeadOut = NULL;
	}
	Chn_UnLock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
#ifdef AUDIO_OUT_DEBUG
	if(NULL != file_play_1)
		fclose(file_play_1);
	if(NULL != file_play_2)
		fclose(file_play_2);
#endif

	return 0;
}

int IMP_AO_SendFrame(int audioDevId, int aoChn, IMPAudioFrame *data, IMPBlock block)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(aoChn > MAX_AUDIO_OUT_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(!audioDev[audioDevId].audioOutChn[aoChn].enable) {
		IMP_LOG_ERR(TAG, "%s %d Audio Channel is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(data->len <= 0)return 0;

	do {
		AudioList *list = Audio_Chn_Prefetch_List(&audioDev[audioDevId].audioOutChn[aoChn].frameHeadIn,
				&audioDev[audioDevId].audioOutChn[aoChn].frameHeadOut, LIST_NOT_USED);
		if(list) {
			if(data->len > MAX_AUDIO_BUF_SIZE) {
				IMP_LOG_ERR(TAG, "%s frame size is too big\n", __func__);
				return -1;
			}
#if 1
			int ret;
			if(IMP_Audio_GetEchoSuppressorState()) {
				if (enable_ao_hpf_suppression) {
					ret = IngenicHPF_Filter(&handle_ao_hpf, (short *)data->virAddr, data->len);
					if (ret != 0) {
						IMP_LOG_ERR(TAG, "IngenicHPF_Filter error.\n");
						return -1;
					}
				}

				if(enable_ao_agc_suppression) {
					int outMicLevel = 0;
					unsigned char saturationWarning = 0;

					if(handle_ao_agc != NULL) {
						ret = IngenicAgc_Process(handle_ao_agc, (short *)data->virAddr,
								agc_sample_o, (short *)data->virAddr, 127,
								&outMicLevel, 0, &saturationWarning, data->len);
						if (ret != 0) {
							IMP_LOG_ERR(TAG, "IngenicAgc_Process ao error.\n");
							return ret;
						}
					}
				}
			}
#endif
			//totallen += data->len;
			//printf("prelen = %d\n",data->len);
			list->buf_size = data->len;
			Audio_Set_Volume((char *)data->virAddr, (char *)list->audio_buf, data->len, 16,
					audioDev[audioDevId].audioOutChn[aoChn].vol.vol);
#ifdef AUDIO_OUT_DEBUG
			if (NULL == file_play_1) {
				file_play_1 = fopen("/tmp/play_1_after_vol.pcm", "wb");
			}
			fwrite((char *)list->audio_buf, 1, data->len, file_play_1);
#endif
			Chn_Lock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
			Audio_Chn_Release_List(&audioDev[audioDevId].audioOutChn[aoChn].frameHeadIn,
					&audioDev[audioDevId].audioOutChn[aoChn].frameHeadOut, LIST_NOT_USED);
			audioDev[audioDevId].audioOutChn[aoChn].status.chnFreeNum--;
			audioDev[audioDevId].audioOutChn[aoChn].status.chnBusyNum++;
			Chn_UnLock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
			Audio_Out_Thread_Post();
			break;
		}
		Audio_Out_Send_Wait();
	} while(block == BLOCK);

	return 0;
}

int IMP_AO_PauseChn(int audioDevId, int aoChn)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(aoChn > MAX_AUDIO_OUT_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	Chn_Lock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
	audioDev[audioDevId].audioOutChn[aoChn].pause = 1;
	Chn_UnLock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);

	return 0;
}

int IMP_AO_ResumeChn(int audioDevId, int aoChn)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(aoChn > MAX_AUDIO_OUT_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	Chn_Lock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
	audioDev[audioDevId].audioOutChn[aoChn].pause = 0;
	Chn_UnLock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);

	return 0;
}

int IMP_AO_ClearChnBuf(int audioDevId, int aoChn)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(aoChn > MAX_AUDIO_OUT_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	Chn_Lock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
	if(Audio_Chn_Clear_List(&audioDev[audioDevId].audioOutChn[aoChn].frameHeadIn,
				&audioDev[audioDevId].audioOutChn[aoChn].frameHeadOut)) {
		IMP_LOG_ERR(TAG, "Audio out clear buf error\n");
		Chn_UnLock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
		return -1;
	}
	audioDev[audioDevId].audioOutChn[aoChn].status.chnFreeNum =
		audioDev[audioDevId].audioOutChn[aoChn].status.chnTotalNum;
	audioDev[audioDevId].audioOutChn[aoChn].status.chnBusyNum = 0;
	Chn_UnLock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
	return 0;
}

int IMP_AO_QueryChnStat(int audioDevId, int aoChn, IMPAudioOChnState *status)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(aoChn > MAX_AUDIO_OUT_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	Chn_Lock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
	memcpy(status, &audioDev[audioDevId].audioOutChn[aoChn].status, sizeof(IMPAudioOChnState));
	Chn_UnLock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
	return 0;

}

static void *Audio_Play_Mute_Thread(void *argv)
{
	int i = 0;
	double * vol = (double *)argv;

	char thread_name[64];
	sprintf(thread_name, "Audio-%s", __func__);
	prctl(PR_SET_NAME, (unsigned long)thread_name);

	if (keep_ao_old_vol >= -20) {
		int interval = 600.0 / ((keep_ao_old_vol + 30) / 10);
		for (i = keep_ao_old_vol; i > -30; i -= 10) {
			*vol = pow(10, ((((double)i) * 0.5) - 30.0) / 20.0);
			usleep(1000*interval);
		}
	}
	*vol = 0;

	return NULL;
}

int IMP_AO_Soft_Mute(int audioDevId, int aoChn)
{
	int ret = -1;
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(aoChn > MAX_AUDIO_OUT_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	double * vol = &(audioDev[audioDevId].audioOutChn[aoChn].vol.vol);
	pthread_t thread_id;
	ret = pthread_create(&thread_id, NULL, Audio_Play_Mute_Thread, (void *)vol);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "[ERROR] %s: pthread_create Audio Play failed\n", __func__);
		return -1;
	}
	pthread_join(thread_id, NULL);

	return 0;
}

static void *Audio_Play_UNMute_Thread(void *argv)
{
	int i = 0;

	char thread_name[64];
	sprintf(thread_name, "Audio-%s", __func__);
	prctl(PR_SET_NAME, (unsigned long)thread_name);

	double * vol = (double *)argv;
	if (keep_ao_old_vol >= -20) {
		int interval = 600.0 / ((keep_ao_old_vol + 30) / 10);
		for (i = -30; i < keep_ao_old_vol; i += 10) {
			*vol = pow(10, ((((double)i) * 0.5) - 30.0) / 20.0);
			usleep(1000*interval);
		}
	}
	*vol = pow(10, ((((double)keep_ao_old_vol) * 0.5) - 30.0) / 20.0);

	return NULL;
}

int IMP_AO_Soft_UNMute(int audioDevId, int aoChn)
{
	int ret = -1;
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(aoChn > MAX_AUDIO_OUT_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	audioDev[audioDevId].audioOutChn[aoChn].vol.vol = pow(10, ((((double)keep_ao_old_vol) * 0.5) - 30.0) / 20.0);

#if 0
	double * vol = &(audioDev[audioDevId].audioOutChn[aoChn].vol.vol);
	pthread_t thread_id;
	ret = pthread_create(&thread_id, NULL, Audio_Play_UNMute_Thread, (void *)vol);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "[ERROR] %s: pthread_create Audio Play failed\n", __func__);
		return -1;
	}
	pthread_join(thread_id, NULL);
#endif

	return 0;
}

int IMP_AO_SetVolMute(int audioDevId, int aoChn, int mute)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(aoChn > MAX_AUDIO_OUT_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	if (mute == 1) {
		audioDev[audioDevId].audioOutChn[aoChn].vol.vol = 0.0;
	} else if (mute == 0) {
		audioDev[audioDevId].audioOutChn[aoChn].vol.vol = keep_ao_old_vol_coef;
	} else {
		IMP_LOG_ERR(TAG, "Invalid AO Mute Value.\n");
		return -1;
	}

	audioDev[audioDevId].audioOutChn[aoChn].vol.mute = mute;

	return 0;
}

int IMP_AO_SetVol(int audioDevId, int aoChn, int aoVol)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(aoChn > MAX_AUDIO_OUT_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	IMPAudioVol *vol = (IMPAudioVol *)malloc(sizeof(IMPAudioVol));
	if(vol == NULL) {
		IMP_LOG_ERR(TAG, "vol is NULL.\n");
		return -1;
	}
	vol->minVol = -30;
	vol->maxVol = 120;
	vol->mute   = 0;

	if (aoVol < vol->minVol) aoVol = -30;
	if (aoVol > vol->maxVol) aoVol = 120;

	if (aoVol == -30) {
		vol->vol = 0.0;
	} else {
		/*dB = 20log(Vx / Vo), step is 0.5dB*/
		vol->vol = pow(10, ((((double)aoVol) * 0.5) - 30.0) / 20.0);
	}

	Chn_Lock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
	audioDev[audioDevId].audioOutChn[aoChn].vol.vol = vol->vol;
	Chn_UnLock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
	keep_ao_old_vol = aoVol;
	keep_ao_old_vol_coef = vol->vol;

	free(vol);
	return 0;
}

int IMP_AO_GetVol(int audioDevId, int aoChn, int *vol)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(aoChn > MAX_AUDIO_OUT_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	Chn_Lock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
	*vol = keep_ao_old_vol;
	Chn_UnLock(&audioDev[audioDevId].audioOutChn[aoChn].chnLock);
	return 0;
}

static int keep_old_ao_gain = 0;
int IMP_AO_SetGain(int audioDevId, int aoChn, int aoGain)
{
	int ret = -1;
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(aoChn > MAX_AUDIO_OUT_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	/*TODO: Just suitable T15 AK4951 Codec,If change Codec,We should modify.*/
	if (aoGain < 0) aoGain = 0;
	if (aoGain > 31) aoGain = 31;
	if (1 == audio_client) {
		ret = audioout_ioctl(audio_fd_client, AUDIOOUT_IOCTL_SNDCTL_EXT_SET_REPLAY_VOLUME, &aoGain);
		if (ret) {
			IMP_LOG_ERR(TAG, "Set Play Volume Error.\n");
			ret = -1;
		}
	} else {
		ret = ioctl(audio_fd, SNDCTL_EXT_SET_REPLAY_VOLUME, &aoGain);
		if (ret) {
			IMP_LOG_ERR(TAG, "Set Play Volume Error.\n");
			ret = -1;
		}
	}

	keep_old_ao_gain = aoGain;

	return 0;
}

int IMP_AO_GetGain(int audioDevId, int aoChn, int *aoGain)
{
	if(audioDevId > MAX_AUDIO_DEV) {
		IMP_LOG_ERR(TAG, "Invalid audio device ID.\n");
		return -1;
	}

	if(!audioDev[audioDevId].aoEnable) {
		IMP_LOG_ERR(TAG, "[%s] [%d] Audio device is no enabled.\n", __func__, __LINE__);
		return -1;
	}

	if(aoChn > MAX_AUDIO_OUT_CHN) {
		IMP_LOG_ERR(TAG, "Invalid audio channel.\n");
		return -1;
	}

	*aoGain = keep_old_ao_gain;

	return 0;
}

int IMP_Audio_Mode_Set(int mode)
{
	if (0 != mode) {
		audio_client = 1;
	} else {
		audio_client = 0;
	}
	return 0;

}
int IMP_Audio_Client_Init(void)
{
	return audio_shm_init();
}
int IMP_Audio_Client_Deinit(void)
{
	return audio_shm_deinit();
}
